الوثائق
رسم حلالا
2رسومات تخطيطية مقيدة تم حلها بواسطة Levenberg–Marquardt، مع درجات الحرية والنقاط الحرة والتعارضات المسماة.
مترجم تلقائيًا من الإنجليزية. عرض الأصل بالإنجليزية
ماذا يفعل الحلال
يقوم برنامج حل الرسومات بتحويل مجموعة من النقاط والخطوط والدوائر والأقواس بالإضافة إلى القيود الهندسية والأبعاد إلى إحداثيات ملموسة. يعيش في packages/core/src/cad/sketch.ts (522 سطر) ويتم التصدير من @cadcamfun/core كـ solveSketch, sketchProfiles و polygonArea. التاريخ البارامتري يحل تعبيراته إلى أرقام أولا، ثم يستدعي الحل؛ يتم تغذية الملامح التي تم حلها بالبثق والتدوير والجيوب.
| مدخل | الإخراج |
|---|---|
| الكيانات: النقاط والخطوط والدوائر والأقواس | نفس الكيانات مع الإحداثيات وأنصاف الأقطار التي تم حلها |
| القيود: 24 أنواع، مسماة اختياريًا | الحالة، درجات الحرية، النقاط الحرة، المعادلات الزائدة، الصراعات |
| options: maxIterations (200), tolerance (1e-9) | التكرارات المستخدمة وأكبر المتبقية |
نموذج البيانات
النقاط هي المجهولة. يشير كل خط ودائرة وقوس إلى نقاط حسب المعرف، لذا فإن الخطين اللذين يشتركان في النهاية يشتركان في النقطة ويتحركان معًا. تضيف الدائرة شيئًا آخر غير معروف، وهو نصف قطرها. القوس هو مركز ونقطتين على الدائرة، عكس اتجاه عقارب الساعة من start إلى end; نصف قطرها هو |البداية − المركز| ويضيف الحل حفظ المعادلة الخاصة به end على نفس الدائرة.
export interface SketchPoint { id: string; kind: "point"; x: number; y: number }
export interface SketchLine { id: string; kind: "line"; p1: PointId; p2: PointId }
export interface SketchCircle { id: string; kind: "circle"; center: PointId; radius: number }
/** Counter-clockwise from `start` to `end` about `center`. */
export interface SketchArc { id: string; kind: "arc"; center: PointId; start: PointId; end: PointId }
export interface Sketch {
readonly entities: ReadonlyArray<SketchEntity>
readonly constraints: ReadonlyArray<Constraint>
} تم وضع المتجه المجهول مثل كل نقطة x, y بترتيب الكيان، ثم كل دائرة r. مستطيل من أربع نقاط له 8 مجهول؛ أضف دائرة بمركزها الخاص ولها 11.
كتالوج القيود
يساهم كل قيد بمعادلات متبقية تكون صفرًا عند الاحتفاظ به. يمكن أن يحمل القيد id; بدون واحد يتم تسميته type#index (على سبيل المثال vertical#2) في تقارير الصراع.
| يكتب | الحقول | معنى | المعادلات |
|---|---|---|---|
fixed | نقطة، س؟، ذ؟ | دبابيس نقطة (إما التنسيق اختياري) | 1–2 |
coincident | a, b | نقطتان واحدة | 2 |
horizontal / vertical | خط | الخط مستوي أو راسيا | 1 |
horizontalPoints / verticalPoints | a, b | نقطتين حصة ص / س | 1 |
distance | أ، ب، القيمة | المسافة من نقطة إلى نقطة | 1 |
distanceX / distanceY | أ، ب، القيمة | وقعت إزاحة على طول محور واحد | 1 |
length | الخط، القيمة | طول الخط | 1 |
parallel / perpendicular | a, b | بين سطرين | 1 |
equalLength | a, b | سطرين | 1 |
equalRadius | a, b | دائرتين أو أقواس | 1 |
radius / diameter | دائرة، قيمة | حجم الدائرة أو القوس | 1 |
angle | أ، ب، القيمة | من الخط أ إلى الخط ب، عكس اتجاه عقارب الساعة، راديان | 1 |
pointOnLine / pointOnCircle | نقطة، خط | دائرة | حدوث | 1 |
midpoint | نقطة، خط | نقطة في منتصف السطر | 2 |
symmetric | أ، ب، سطر | زوج المرآة حول الخط | 2 |
tangentLineCircle | خط، دائرة | الخط يلامس الدائرة | 1 |
tangentCircles | أ، ب، داخلي؟ | خارجي r1 + r2 أو داخلي |r1 − r2| | 1 |
concentric | a, b | تتزامن المراكز | 2 |
الحل: ليفنبرج-ماركوارت
يقوم الحل بتقليل مجموع القيم التربيعية المتبقية باستخدام Levenberg-Marquardt والفرق المركزي Jacobian. كل خطوة تحل المعادلات العادية المخمدة عن طريق تحليل تشوليسكي؛ يتم قبول الخطوة التي تخفض المتبقي ويسقط التخميد، والخطوة التي لا يتم رفضها وينمو التخميد. نظرًا لأن كل خطوة هي أصغر تغيير يقلل من البقايا، فإن النقطة المسحوبة تسحب بقية الرسم بنفس الطريقة التي تظهر بها على لوحة الرسم.
- أنشئ الدالة المتبقية لكل قيد، وسجل القيد الذي يمتلك كل معادلة.
- Start with λ = 1e-3. Solve (JᵀJ + λ·max(1, diag)) Δx = −Jᵀr; if Cholesky fails, multiply λ by 10 and retry.
- Accept the step if the residual norm drops (λ = max(λ/5, 1e-12)); otherwise λ ×= 10.
- توقف عندما يكون المتبقي أدناه
tolerance(1e-9) أو بعد ذلكmaxIterations(200). - Analyse the Jacobian at the solution: rank by Gaussian elimination with full pivoting (tolerance 1e-7), null-space basis, free unknowns (component above 1e-6).
| ثابت | قيمة | أين |
|---|---|---|
| الحد الأقصى للتكرارات الافتراضية | 200 | SolveOptions |
| التسامح الافتراضي | 1e-9 | SolveOptions |
| التخميد الأولي π | 1e-3 | solveSketch |
| أرضية التخميد | 1e-12 | solveSketch |
| حلها إذا المتبقية ≥ | 1e-6 | حالة |
| التسامح رتبة | 1e-7 | nullSpace |
| عتبة حرة غير معروفة | 1e-6 | نقاط مجانية |
| عتبة التعارض (لكل معادلة) | 1e-6 | الصراعات |
الحالة والتشخيص
بعد الحل درجات الحرية = المجهولة - الرتبة والمعادلات الزائدة = المعادلات - الرتبة. تتبع الحالة هذين الرقمين ومن ما إذا كان المتبقي قد وصل إلى 1e-6.
| حالة | حالة | ما يجب القيام به |
|---|---|---|
fully-constrained | تم الحل، dof 0، بدون تكرار | لا شئ |
under-constrained | تم الحل، dof > 0 | إضافة أبعاد للمعرفات في free |
over-constrained | تم الحل، زائدة عن الحاجة > 0 | إزالة قيد مكرر (غير ضار ولكنه صاخب) |
conflicting | لم يتم حلها، زائدة عن الحاجة > 0 | إصلاح القيد في conflicts[0] |
failed | لم تحل، لا التكرار | ابدأ بالقرب من الشكل المقصود أو ارفع الحد الأقصى للتكرارات |
These results come from running the solver on a rectangle drawn slightly askew: a(0,0) b(38,2) c(41,27) d(−1,24), with a fixed at the origin and the four lines horizontal/vertical.
// + length(l1) = 40
{"status":"under-constrained","dof":1,"free":["c","d"],"iterations":3,"redundant":0}
// + length(l2) = 25
{"status":"fully-constrained","dof":0,"iterations":3,"residual":7.95e-12,
"points":[["a",0,0],["b",40,0],["c",40,25],["d",0,25]]}
// + length(l3) = 40 (implied by the others)
{"status":"over-constrained","dof":0,"redundant":1}
// + length(l3) = 30 named "bad"
{"status":"conflicting","redundant":1,
"conflicts":["bad","vertical#2","length#5","vertical#4"],
"message":"The constraints cannot all hold; the worst miss is bad"} أقل تغيير، وليس أقلها مفاجأة
With height still free, moving c to (55, 31) and solving gave c = (40, 27.5) and d = (0, 27.5): the free height settled at the mean of the dragged c.y (31) and the old d.y (24), because Levenberg–Marquardt makes the smallest change that satisfies the constraints.
API
export const solveSketch: (sketch: Sketch, options?: SolveOptions) => SolveResult
export const sketchProfiles: (sketch: Sketch, tolerance?: number /* 0.01 */) =>
{ readonly loops: Vec2[][]; readonly open: Vec2[][] }
export const polygonArea: (ring: ReadonlyArray<Vec2>) => number // positive counter-clockwise
export class SketchError extends Error {}
export interface SolveResult {
sketch: Sketch; status: SolveStatus; dof: number; iterations: number
residual: number // largest absolute residual left
free: string[] // points and circles that can still move
conflicts: string[] // worst first, only when not solved
redundant: number
message?: string
} import { solveSketch, sketchProfiles, polygonArea } from "@cadcamfun/core"
const P = (id: string, x: number, y: number) => ({ id, kind: "point" as const, x, y })
const L = (id: string, p1: string, p2: string) => ({ id, kind: "line" as const, p1, p2 })
const result = solveSketch({
entities: [P("a", 0, 0), P("b", 38, 2), P("c", 41, 27), P("d", -1, 24),
L("l1", "a", "b"), L("l2", "b", "c"), L("l3", "c", "d"), L("l4", "d", "a"),
P("o", 20, 12), { id: "h", kind: "circle", center: "o", radius: 4 }],
constraints: [
{ type: "fixed", point: "a", x: 0, y: 0 },
{ type: "horizontal", line: "l1" }, { type: "vertical", line: "l2" },
{ type: "horizontal", line: "l3" }, { type: "vertical", line: "l4" },
{ type: "length", line: "l1", value: 40 }, { type: "length", line: "l2", value: 25 },
{ type: "distanceX", a: "a", b: "o", value: 20 }, { type: "distanceY", a: "a", b: "o", value: 12.5 },
{ type: "diameter", circle: "h", value: 10 },
],
})
result.status // "fully-constrained", dof 0
const { loops } = sketchProfiles(result.sketch)
loops.map((l) => [l.length, polygonArea(l)])
// [[50, 78.333], [4, 1000]] circle: 50 points; rectangle: 4 points, area 1000 من الرسم إلى الملفات الشخصية
sketchProfiles تنتهي خطوط السلاسل والأقواس من خلال معرفات النقاط المشتركة الخاصة بها في حلقات مغلقة، وتعيد كل دائرة كحلقة خاصة بها. يصبح القوس نقاطًا متباعدة بحيث يبقى الوتر بداخله tolerance of the true arc (n = ⌈sweep / (2·acos(1 − tol/r))⌉, at least 2); a full circle gets at least 8 points. A chain that does not close is returned in open بدلا من أن يتم إسقاطها.
تم حل الفتحة أعلاه كـ over-constrained مع dof 0 و1 معادلة زائدة عن الحاجة: صنع top يتم تضمين الأفقي بمجرد أن يكون c1 –a1 وc2 –b1 عموديًا وc1، c2 يتشاركان في y. الحل لا يزال ناجحا. تشير الحالة إلى التكرار فقط.
استكشاف الأخطاء وإصلاحها
| أعراض | سبب | يصلح |
|---|---|---|
SketchError: Duplicate entity id | كيانان يشتركان في معرف | المعرفات فريدة عبر النقاط والخطوط والدوائر والأقواس |
SketchError: Unknown unknown | يسمي القيد نقطة أو دائرة غير موجودة | تحقق من المعرفات في القيد |
conflicting مع قائمة الصراعات الطويلة | بعد واحد خاطئ يسحب عدة معادلات من الصفر | إصلاح الإدخال الأول؛ والباقي عادة ما يكون ضمانات |
failed | البداية بعيدة عن أي حل | التقرب من الشكل المقصود؛ رفع الحد الأقصى للتكرارات |
الملف الشخصي مفقود من loops | السلسلة لا تغلق: إنها موجودة open | جعل نقاط النهاية معرفات مشتركة، أو إضافة قيد متزامن |
| تحتوي الدائرة على عدد قليل جدًا من النقاط | التسامح خشن جدًا بالنسبة لنصف قطر صغير | تمرير تسامح أصغر إلى SketchProfiles |