दस्तावेज़
स्केच सॉल्वर
स्वतंत्रता की डिग्री, मुक्त अंक और नामित संघर्षों के साथ, लेवेनबर्ग-मार्क्वार्ट द्वारा हल किए गए प्रतिबंधित 2डी रेखाचित्र।
अंग्रेज़ी से स्वचालित रूप से अनुवादित। अंग्रेज़ी मूल देखें
सॉल्वर क्या करता है
स्केच सॉल्वर बिंदुओं, रेखाओं, वृत्तों और चापों के साथ-साथ ज्यामितीय और आयामी बाधाओं के एक सेट को ठोस निर्देशांक में बदल देता है। वो में रहता है 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 | दो अंक y/x साझा करते हैं | 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 |
समाधान: लेवेनबर्ग-मार्क्वार्ट
सॉल्वर लेवेनबर्ग-मार्क्वार्ड और एक केंद्रीय-अंतर जैकोबियन के साथ वर्ग अवशेषों के योग को कम करता है। प्रत्येक चरण चोलेस्की गुणनखंडन द्वारा अवमंदित सामान्य समीकरणों को हल करता है; एक कदम जो अवशिष्ट को कम करता है उसे स्वीकार कर लिया जाता है और अवमंदन कम हो जाता है, एक ऐसा कदम जो अवशिष्ट को कम नहीं करता है उसे अस्वीकार कर दिया जाता है और अवमंदन बढ़ता है। क्योंकि प्रत्येक चरण सबसे छोटा परिवर्तन है जो अवशेषों को कम करता है, एक खींचा हुआ बिंदु शेष स्केच को उसी तरह खींचता है जैसे वह एक ड्राइंग बोर्ड पर होता है।
- प्रत्येक बाधा के अवशिष्ट फ़ंक्शन का निर्माण करें, और रिकॉर्ड करें कि कौन सी बाधा प्रत्येक समीकरण का स्वामी है।
- 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(1ई-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).
| स्थिर | कीमत | कहाँ |
|---|---|---|
| maxIterations डिफ़ॉल्ट | 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 | अंतिम बिंदु साझा आईडी बनाएं, या एक संयोग बाधा जोड़ें |
| सर्कल में बहुत कम अंक हैं | एक छोटे दायरे के लिए सहनशीलता बहुत कठिन है | स्केचप्रोफ़ाइल्स के लिए एक छोटी सहनशीलता पास करें |