Odontogram & Periodontics

v0.1.0

Interactive 5-surface anatomical tooth charting, morphological validation rules, and 6-point periodontal probing matrix.

Interactive Odontogram & Periodontics

The Odontogram & Periodontics bounded context provides anatomical dental charting, multi-system tooth notation, surface-level condition mapping, and full-mouth 6-point periodontal disease diagnostics.


Tooth Notation Systems & Dentition

Obelawium Dental natively supports international standard dental notation schemes:

use Obelaw\Ium\Dental\Enums\ToothNotationSystem;
use Obelaw\Ium\Dental\Enums\DentitionType;

enum ToothNotationSystem: string
{
    case UNIVERSAL = 'universal'; // 1-32, A-T (American Standard)
    case FDI = 'fdi';             // Two-digit ISO 3950 (11-48, 51-85)
    case PALMER = 'palmer';       // Quadrant grid notation
}

enum DentitionType: string
{
    case PERMANENT = 'permanent'; // 32 adult teeth
    case DECIDUOUS = 'deciduous'; // 20 primary / pediatric teeth
}

The ToothNumber Value Object

The ToothNumber value object guarantees strict typing and automatic bidirectional conversion across notation standards:

use Obelaw\Ium\Dental\ValueObjects\ToothNumber;

// Instantiate from Universal number
$molar = ToothNumber::fromUniversal(14); // Upper Left First Molar

echo $molar->value;           // 14
echo $molar->toFdi();         // 26 (FDI Quadrant 2, Tooth 6)
echo $molar->isAnterior();    // false (Posterior Molar)
echo $molar->isMaxillary();   // true (Upper Arch)

// Instantiate from FDI Two-Digit (ISO 3950)
$canine = ToothNumber::fromFdi(13); // Upper Right Canine
echo $canine->toUniversal();  // 6
echo $canine->isAnterior();   // true

5-Surface Functional Anatomical Charting

Restorations, active carious lesions, and sealants are charted across five anatomical surfaces:

  • Mesial (M): The surface facing toward the dental midline.
  • Distal (D): The surface facing away from the midline.
  • Occlusal (O): The chewing surface of posterior teeth (molars and premolars).
  • Incisal (I): The cutting biting edge of anterior teeth (incisors and canines).
  • Buccal / Facial (B / F): The surface facing the cheek or lips.
  • Lingual / Palatal (L / P): The surface facing the tongue or palate.
use Obelaw\Ium\Dental\ValueObjects\SurfaceSet;
use Obelaw\Ium\Dental\Enums\ToothSurface;

$surfaces = SurfaceSet::from([
    ToothSurface::MESIAL->value,
    ToothSurface::OCCLUSAL->value,
    ToothSurface::DISTAL->value,
]); // MOD restoration set

Morphological Surface Invariant

To preserve clinical accuracy, the domain enforces strict anatomical morphology rules through $surfaces->validateForTooth($tooth):

               [Tooth Morphology Validation]
                             |
              Is the tooth Anterior or Posterior?
                             |
         +-------------------+-------------------+
         |                                       |
    [Anterior]                              [Posterior]
(Incisors / Canines)                    (Premolars / Molars)
         |                                       |
  Cannot have 'O'                         Cannot have 'I'
 (Occlusal surface)                       (Incisal edge)
         |                                       |
If 'O' present:                         If 'I' present:
THROWS InvalidToothMorphologyException  THROWS InvalidToothMorphologyException
use Obelaw\Ium\Dental\Exceptions\InvalidToothMorphologyException;

// Example: Incisor cannot have an Occlusal surface
$centralIncisor = ToothNumber::fromUniversal(8); // Upper Right Central Incisor
$invalidSurfaces = SurfaceSet::from([ToothSurface::OCCLUSAL->value]);

// Throws InvalidToothMorphologyException: Tooth 8 is anterior and cannot have an occlusal surface.
$invalidSurfaces->validateForTooth($centralIncisor);

Tooth Clinical Conditions

The condition of each tooth or surface is tracked via ToothCondition:

use Obelaw\Ium\Dental\Enums\ToothCondition;

enum ToothCondition: string
{
    case HEALTHY = 'healthy';
    case CARIES = 'caries';
    case RESTORATION = 'restoration';
    case CROWN = 'crown';
    case ENDO = 'endo';             // Root Canal Treatment
    case IMPLANT = 'implant';       // Titanium osteointegrated implant
    case MISSING = 'missing';       // Congenitally absent
    case EXTRACTED = 'extracted';   // Surgically removed
    case FRACTURED = 'fractured';
    case IMPACTED = 'impacted';     // Third molar impaction
}

6-Point Periodontal Probing Matrix

Periodontal examinations evaluate periodontal pocket depth (PPD), gingival recession, bleeding, furcation involvement, and tooth mobility across six anatomical sites per tooth:

  1. MB: Mesiobuccal
  2. B: Buccal / Mid-buccal
  3. DB: Distobuccal
  4. ML: Mesiolingual
  5. L: Lingual / Mid-lingual
  6. DL: Distolingual

Clinical Attachment Level (CAL) Computation

The domain automatically computes the true Clinical Attachment Level (CAL):

$$\text{CAL} = \text{Probing Pocket Depth (PPD)} + \text{Gingival Recession}$$

If PPD is 5mm and Gingival Recession is 2mm, the resulting CAL is 7mm, indicating severe clinical periodontal attachment loss.

use Obelaw\Ium\Dental\ValueObjects\PeriodontalProbingMatrix;
use Obelaw\Ium\Dental\Enums\ProbingPoint;

$matrix = new PeriodontalProbingMatrix(
    depths: [
        ProbingPoint::MESIOBUCCAL->value => 4,
        ProbingPoint::BUCCAL->value => 3,
        ProbingPoint::DISTOBUCCAL->value => 5,
        ProbingPoint::MESIOLINGUAL->value => 3,
        ProbingPoint::LINGUAL->value => 2,
        ProbingPoint::DISTOLINGUAL->value => 4,
    ],
    bleeding: [
        ProbingPoint::MESIOBUCCAL->value => true,  // Bleeding on probing (BOP)
        ProbingPoint::DISTOBUCCAL->value => true,
    ]
);

Fluent Service Operations

All charting operations are performed via ium()->dental()->odontograms():

1. Creating a New Odontogram Chart

use Obelaw\Ium\Dental\Enums\ToothNotationSystem;
use Obelaw\Ium\Dental\Enums\DentitionType;

$chart = ium()->dental()->odontograms()->createChart(
    patientId: $patient->id,
    system: ToothNotationSystem::UNIVERSAL,
    type: DentitionType::PERMANENT
);

2. Charting Tooth Conditions & Surfaces

use Obelaw\Ium\Dental\ValueObjects\ToothNumber;
use Obelaw\Ium\Dental\ValueObjects\SurfaceSet;
use Obelaw\Ium\Dental\Enums\ToothCondition;
use Obelaw\Ium\Dental\Enums\ToothSurface;

$molar = ToothNumber::fromUniversal(19); // Lower Left First Molar
$surfaces = SurfaceSet::from([
    ToothSurface::MESIAL->value,
    ToothSurface::OCCLUSAL->value,
]);

$chartedTooth = ium()->dental()->odontograms()->markSurface(
    chartId: $chart->id,
    tooth: $molar,
    surfaces: $surfaces,
    condition: ToothCondition::CARIES,
    notes: 'Incipient dentin caries on MO margin.'
);

3. Recording Periodontal Probing

use Obelaw\Ium\Dental\ValueObjects\ToothNumber;
use Obelaw\Ium\Dental\ValueObjects\PeriodontalProbingMatrix;

$tooth = ToothNumber::fromUniversal(30); // Lower Right First Molar

$perioPoint = ium()->dental()->odontograms()->recordProbing(
    chartId: $chart->id,
    tooth: $tooth,
    matrix: $matrix,
    recession: ['MB' => 1, 'B' => 1, 'DB' => 2, 'ML' => 0, 'L' => 0, 'DL' => 1],
    furcation: 1, // Class I furcation
    mobility: 1,  // Grade 1 mobility
    notes: 'Subgingival calculus noted on mesial root.'
);

4. Fetching the Latest Patient Chart

$latestChart = ium()->dental()->odontograms()->getLatestChart($patient->id);
$teeth = $latestChart->teeth; // Collection of OdontogramTooth records

Emitted Domain Events

Event ClassTriggerPayload
ToothChartedDispatched after tooth conditions or surfaces are updated.OdontogramTooth $tooth
PerioExamRecordedDispatched when a periodontal point or exam is recorded.PeriodontalPoint $point