TEORİDEN UYGULAMAYA · PYTHON EŞLİKÇİSİ

Çember, Kiriş ve Teğet

Prof. Dr. Zühal Küçükarslan Yüzbaşı · Prof. Dr. Bahadır Yüzbaşı

Bölüm 10 · v1.0 · Türkçe

Bu bölümün laboratuvarı, kitaptaki örnekleri yeniden üretmek ve sayısal sonuçları incelemek için hazırlanmıştır. Matematiksel tanımlar, ispatlar ve sorular kitapta yer alır.

Laboratuvarı indir (.py) Tam Python paketi (.zip)

Laboratuvar kodu

"""Bölüm 10 laboratuvarı: dairesel CAD güvenlik bölgesi geometrisi.

Model iki boyutlu ve deterministiktir; koordinatlar milimetredir. Bilinen bir
çember ile üç sonsuz takım yolu arasındaki kesen, teğet ve ayrık ilişkileri
hesaplanır. Sonuç; takım yarıçapı, hareket zamanı, üretim toleransı, parça
deformasyonu veya gerçek bir çarpışma güvenlik kararı içermez.
"""

from __future__ import annotations

import numpy as np

from agbook import (
    canonical_array_sha256,
    circle_line_intersections,
    general_circle_diagnostics,
    polar_line_of_point,
    rotation_matrix_2d,
    tangent_points_from_point,
)


ABSOLUTE_TOLERANCE_MM = 1e-9
RELATIVE_TOLERANCE = 1e-12


def transform_line(line: np.ndarray, rotation: np.ndarray, translation: np.ndarray) -> np.ndarray:
    """``x' = Qx+b`` altında genel doğru katsayılarını taşır."""

    moved_normal = rotation @ line[:2]
    moved_offset = line[2] - float(np.dot(moved_normal, translation))
    return np.r_[moved_normal, moved_offset]


def main() -> None:
    center_mm = np.array([120.0, 80.0])
    radius_mm = 45.0
    reference_scale_mm = 200.0
    general_coefficients = np.array(
        [
            1.0,
            -2.0 * center_mm[0],
            -2.0 * center_mm[1],
            float(np.dot(center_mm, center_mm) - radius_mm**2),
        ]
    )
    general = general_circle_diagnostics(general_coefficients)

    paths = {
        "kesen": np.array([0.0, 1.0, -100.0]),
        "teget": np.array([1.0, 0.0, -165.0]),
        "ayrik": np.array([0.0, 1.0, -135.0]),
    }
    diagnostics = {
        name: circle_line_intersections(
            center_mm,
            radius_mm,
            line,
            absolute_tolerance=ABSOLUTE_TOLERANCE_MM,
            relative_tolerance=RELATIVE_TOLERANCE,
            reference_scale=reference_scale_mm,
        )
        for name, line in paths.items()
    }

    inspection_point_mm = np.array([200.0, 140.0])
    contacts = tangent_points_from_point(
        center_mm,
        radius_mm,
        inspection_point_mm,
        absolute_tolerance=ABSOLUTE_TOLERANCE_MM,
        relative_tolerance=RELATIVE_TOLERANCE,
        reference_scale=reference_scale_mm,
    )
    polar = polar_line_of_point(center_mm, radius_mm, inspection_point_mm)
    polar_contact_residuals = contacts.points @ polar[:2] + polar[2]

    rotation = rotation_matrix_2d(np.deg2rad(27.0))
    translation_mm = np.array([400.0, -150.0])
    moved_center_mm = rotation @ center_mm + translation_mm
    moved_paths = {
        name: transform_line(line, rotation, translation_mm)
        for name, line in paths.items()
    }
    moved_diagnostics = {
        name: circle_line_intersections(
            moved_center_mm,
            radius_mm,
            line,
            absolute_tolerance=ABSOLUTE_TOLERANCE_MM,
            relative_tolerance=RELATIVE_TOLERANCE,
            reference_scale=reference_scale_mm,
        )
        for name, line in moved_paths.items()
    }

    chord_differences = [
        abs(
            (moved_diagnostics[name].chord_length or 0.0)
            - (diagnostics[name].chord_length or 0.0)
        )
        for name in paths
    ]
    distance_differences = [
        abs(moved_diagnostics[name].center_distance - diagnostics[name].center_distance)
        for name in paths
    ]

    print("birim: mm")
    print("model: sentetik_iki_boyutlu_cad_geometrisi")
    print("tam_carpisma_modeli_mi: False")
    print("cember_turu:", general.locus_type)
    print("merkez_mm:", np.round(general.center, 9))
    print("yaricap_mm:", f"{general.radius:.9f}")
    for name, result in diagnostics.items():
        print(
            f"yol_{name}:",
            result.relation,
            "merkez_uzakligi_mm=" + f"{result.center_distance:.9f}",
            "kiris_uzunlugu_mm="
            + ("yok" if result.chord_length is None else f"{result.chord_length:.9f}"),
        )
        print(f"yol_{name}_kesisimler_mm:", np.round(result.points, 9))
    print("dis_nokta_sinifi:", contacts.relation)
    print("teget_uzunlugu_mm:", f"{contacts.tangent_length:.9f}")
    print("temas_noktalari_mm:", np.round(contacts.points, 9))
    print("kutupsal_kanonik:", np.round(polar, 12))
    print("en_buyuk_kutupsal_artigi_mm:", f"{np.max(np.abs(polar_contact_residuals)):.3e}")
    print("rijit_siniflar_korundu_mu:", all(
        diagnostics[name].relation == moved_diagnostics[name].relation for name in paths
    ))
    print("en_buyuk_rijit_uzaklik_farki_mm:", f"{max(distance_differences):.3e}")
    print("en_buyuk_rijit_kiris_farki_mm:", f"{max(chord_differences):.3e}")

    signature_values = np.concatenate(
        [
            center_mm,
            np.array([radius_mm, reference_scale_mm, ABSOLUTE_TOLERANCE_MM, RELATIVE_TOLERANCE]),
            general_coefficients,
            *(line for line in paths.values()),
            inspection_point_mm,
            rotation.ravel(),
            translation_mm,
        ]
    )
    print("bilimsel_imza:", canonical_array_sha256(signature_values))


if __name__ == "__main__":
    main()
Doğrulama çalıştırmasının çıktısı
birim: mm
model: sentetik_iki_boyutlu_cad_geometrisi
tam_carpisma_modeli_mi: False
cember_turu: circle
merkez_mm: [120.  80.]
yaricap_mm: 45.000000000
yol_kesen: secant merkez_uzakligi_mm=20.000000000 kiris_uzunlugu_mm=80.622577483
yol_kesen_kesisimler_mm: [[160.31128874 100.        ]
 [ 79.68871126 100.        ]]
yol_teget: tangent merkez_uzakligi_mm=45.000000000 kiris_uzunlugu_mm=0.000000000
yol_teget_kesisimler_mm: [[165.  80.]]
yol_ayrik: disjoint merkez_uzakligi_mm=55.000000000 kiris_uzunlugu_mm=yok
yol_ayrik_kesisimler_mm: []
dis_nokta_sinifi: outside
teget_uzunlugu_mm: 89.302855497
temas_noktalari_mm: [[160.31177098  60.00097202]
 [112.08822902 124.29902798]]
kutupsal_kanonik: [   0.8     0.6  -164.25]
en_buyuk_kutupsal_artigi_mm: 0.000e+00
rijit_siniflar_korundu_mu: True
en_buyuk_rijit_uzaklik_farki_mm: 5.684e-14
en_buyuk_rijit_kiris_farki_mm: 2.842e-14
bilimsel_imza: f197d76233595fdea9ed0f49ec7a3a5814f173bbd9e3b6ca083309afa47a0075
Metindeki Python kodları

Kod dosyasını indir

# Kitaptaki kod blokları; bu dosya içinde sırayla çalıştırılır.
# --- 1. Bölüm 10: doğru--çember ve kutupsal tanısı ---
import numpy as np
from agbook import (
    circle_line_intersections,
    polar_line_of_point,
    tangent_points_from_point,
)

C = np.array([120.0, 80.0])  # mm
r = 45.0                     # mm
paths = {
    "kesen": [0.0, 1.0, -100.0],
    "teget": [1.0, 0.0, -165.0],
    "ayrik": [0.0, 1.0, -135.0],
}
tol = dict(
    absolute_tolerance=1e-9,
    relative_tolerance=1e-12,
    reference_scale=200.0,
)
for name, line in paths.items():
    out = circle_line_intersections(C, r, line, **tol)
    print(name, out.relation, out.center_distance,
          out.chord_length)

A = np.array([200.0, 140.0])  # mm
contacts = tangent_points_from_point(C, r, A, **tol)
polar = polar_line_of_point(C, r, A)
print(contacts.relation, contacts.tangent_length)
print(polar)

Kaydedilen çıktı

1. Bölüm 10: doğru--çember ve kutupsal tanısı
kesen secant 20.0 80.62257748298549
teget tangent 45.0 0.0
ayrik disjoint 55.0 None
outside 89.30285549745876
[   0.8     0.6  -164.25]
Çözümlerdeki Python kodları

Kod dosyasını indir

# Kitaptaki kod blokları; bu dosya içinde sırayla çalıştırılır.
# --- 1. Genel çember sınıfları ve ölçek değişmezliği ---
import numpy as np
from agbook import general_circle_diagnostics

cases = [
    [1., -4., 2., -4.],  # r^2=9
    [1., -4., 2.,  5.],  # r^2=0
    [1., -4., 2.,  7.],  # r^2=-2
]
for coefficients in cases:
    first = general_circle_diagnostics(coefficients)
    second = general_circle_diagnostics(
        -7.0 * np.array(coefficients)
    )
    print(first.locus_type, first.center,
          first.radius_squared)
    print(np.max(np.abs(first.center-second.center)),
          first.radius_squared-second.radius_squared)

# --- 2. Ayrık, teğet ve kesen doğrular ---
from agbook import circle_line_intersections

tol = dict(
    absolute_tolerance=1e-12,
    relative_tolerance=1e-12,
    reference_scale=10.0,
)
for line in ([0, 1, -8], [0, 1, -7], [1, 0, -1]):
    out = circle_line_intersections(
        [1, 2], 5, line, **tol
    )
    print(out.relation, out.points.shape,
          out.chord_length)
    print(out.line_residuals, out.radial_residuals)

# --- 3. Temas, diklik ve kutupsal artıkları ---
import numpy as np
from agbook import (
    polar_line_of_point,
    tangent_points_from_point,
)

tol = dict(
    absolute_tolerance=1e-12,
    relative_tolerance=1e-12,
    reference_scale=20.0,
)
C, r, A = np.zeros(2), 5.0, np.array([13., 0.])
out = tangent_points_from_point(C, r, A, **tol)
polar = polar_line_of_point(C, r, A)
print(out.relation, out.points)
print(out.radial_residuals)
print(out.orthogonality_residuals)
print(out.points @ polar[:2] + polar[2])

inside = tangent_points_from_point(
    C, r, [1., 0.], **tol
)
print(inside.relation, inside.points.shape)

# --- 4. Rijit çerçevede kesişim değişmezliği ---
import numpy as np
from agbook import (
    circle_line_intersections,
    rotation_matrix_2d,
)

tol = dict(
    absolute_tolerance=1e-12,
    relative_tolerance=1e-12,
    reference_scale=10.0,
)
C = np.array([1., 2.])
line = np.array([0., 1., -4.])
before = circle_line_intersections(C, 5., line, **tol)

Q = rotation_matrix_2d(np.deg2rad(37.0))
b = np.array([8., -3.])
Cm = Q @ C + b
nm = Q @ line[:2]
linem = np.r_[nm, line[2] - nm @ b]
after = circle_line_intersections(Cm, 5., linem, **tol)

expected = before.points @ Q.T + b
print(before.relation, after.relation)
print(before.center_distance, after.center_distance)
print(before.chord_length, after.chord_length)
print(expected)
print(after.points)

Kaydedilen çıktı

1. Genel çember sınıfları ve ölçek değişmezliği
circle [ 2. -1.] 9.0
0.0 0.0
point [ 2. -1.] 0.0
0.0 0.0
empty [ 2. -1.] -2.0
0.0 0.0

2. Ayrık, teğet ve kesen doğrular
disjoint (0, 2) None
[] []
tangent (1, 2) 0.0
[0.] [0.]
secant (2, 2) 10.0
[0. 0.] [0. 0.]

3. Temas, diklik ve kutupsal artıkları
outside [[ 1.92307692 -4.61538462]
 [ 1.92307692  4.61538462]]
[0. 0.]
[3.55271368e-15 3.55271368e-15]
[0. 0.]
inside (0, 2)

4. Rijit çerçevede kesişim değişmezliği
secant secant
2.0 2.0
9.16515138991168 9.16515138991168
[[10.05118309  3.55421996]
 [ 2.73156774 -1.96150583]]
[[ 2.73156774 -1.96150583]
 [10.05118309  3.55421996]]

Çalıştırma rehberi

Önce tam Python paketini indirin ve arşivi açın. Aşağıdaki komutları arşivin üst klasöründen başlatın. İlk kurulum internet bağlantısı ve Python 3.12 veya 3.13 gerektirir. Bağımlılıklar sabittir; sistem Python'unu değiştirmemek için ayrı sanal ortam kullanılır.

macOS / Linux
cd analitik-geometri-python-v1.0
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
python labs/bolum_10_cember_kiris_teget.py
python -m pytest
Windows PowerShell
cd analitik-geometri-python-v1.0
py -3.13 -m venv .venv
.venv\Scripts\python.exe -m pip install -e ".[dev]"
.venv\Scripts\python.exe labs/bolum_10_cember_kiris_teget.py
.venv\Scripts\python.exe -m pytest

Laboratuvar dosyaları agbook yardımcı paketini kullanır; tek bir dosyayı indirmek paketi kurmanın yerini tutmaz. Metin/çözüm kodları kendi dosyaları içinde sırayla çalıştırılır; gizli bir notebook oturumu gerekmez. Son ondalık basamaklar platforma göre değişebilir.