diff --git a/languages/ar.json b/languages/ar.json index 2d145c7b..f50f78a9 100644 --- a/languages/ar.json +++ b/languages/ar.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "ينشئ شبكة من الشكل", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "من نقاط المضلع", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "إنشاء متعدد الطيات (Manifold) من مجموعة من نقاط المضلع التي تصف المثلثات.", - "traingle": "مثلث" + "traingle": "مثلث", + "bitbybit.point.stretchPointsDirFromCenter": "تمديد النقاط في اتجاه من المركز", + "stretchPointsDirFromCenter": "تمديد النقاط في اتجاه من المركز", + "bitbybit.point.stretchPointsDirFromCenter_description": "تمديد نقاط متعددة بتوفير نقطة مركزية واتجاه وعامل قياس موحد", + "bitbybit.point.hexGridScaledToFit": "شبكة سداسية مكيفة لتناسب الأبعاد", + "hexGridScaledToFit": "شبكة سداسية مكيفة لتناسب الأبعاد", + "bitbybit.point.hexGridScaledToFit_description": "إنشاء شبكة سداسية ذات قمة مدببة، مع تغيير حجم السداسيات لتناسب الأبعاد المحددة بالضبط. تُرجع كلاً من نقاط المركز ورؤوس كل سداسي (محتمل تغيير حجمه). يتم ترتيب السداسيات حسب العمود أولاً، ثم حسب الصف.", + "nrHexagonsU": "عدد السداسيات U", + "nrHexagonsV": "عدد السداسيات V", + "extendTop": "تمديد أعلى", + "extendBottom": "تمديد أسفل", + "extendLeft": "تمديد يسار", + "extendRight": "تمديد يمين", + "centerGrid": "توسيط الشبكة", + "bitbybit.point.sortPoints": "ترتيب النقاط", + "sortPoints": "ترتيب النقاط", + "bitbybit.point.sortPoints_description": "ترتيب النقاط أبجدياً (X، ثم Y، ثم Z)", + "bitbybit.line.lineLineIntersection": "تقاطع خط-خط", + "lineLineIntersection": "تقاطع خط-خط", + "bitbybit.line.lineLineIntersection_description": "إذا تقاطع خطان، قم بإرجاع نقطة التقاطع", + "line1": "الخط 1", + "line2": "الخط 2", + "checkSegmentsOnly": "التحقق من المقاطع فقط", + "bitbybit.polyline.polylineToLines": "خط متعدد إلى خطوط", + "polylineToLines": "خط متعدد إلى خطوط", + "bitbybit.polyline.polylineToLines_description": "إنشاء الخطوط من الخط المتعدد", + "bitbybit.polyline.polylineToSegments": "خط متعدد إلى مقاطع", + "polylineToSegments": "خط متعدد إلى مقاطع", + "bitbybit.polyline.polylineToSegments_description": "إنشاء المقاطع من الخط المتعدد", + "bitbybit.polyline.polylineSelfIntersection": "تقاطع ذاتي للخط المتعدد", + "polylineSelfIntersection": "تقاطع ذاتي للخط المتعدد", + "bitbybit.polyline.polylineSelfIntersection_description": "إيجاد نقاط التقاطع الذاتي للخط المتعدد", + "bitbybit.polyline.twoPolylineIntersection": "تقاطع خطين متعددين", + "twoPolylineIntersection": "تقاطع خطين متعددين", + "bitbybit.polyline.twoPolylineIntersection_description": "إيجاد نقاط التقاطع بين خطين متعددين.", + "polyline1": "الخط المتعدد 1", + "polyline2": "الخط المتعدد 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "تقسيم إلى هياكل سداسية سلكية", + "subdivideToHexagonWires": "تقسيم إلى هياكل سداسية سلكية", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "يقسم السطح إلى هياكل سداسية سلكية", + "extendUUp": "تمديد U لأعلى", + "extendUBottom": "تمديد U لأسفل", + "extendVUp": "تمديد V لأعلى", + "extendVBottom": "تمديد V لأسفل", + "nrHexagonsInHeight": "عدد السداسيات في الارتفاع", + "nrHexagonsInWidth": "عدد السداسيات في العرض", + "bitbybit.vector.length": "طول المتجه", + "bitbybit.vector.length_description": "حساب طول المتجه", + "bitbybit.point.maxFilletRadius": "أقصى نصف قطر للتقريب", + "maxFilletRadius": "أقصى نصف قطر للتقريب", + "bitbybit.point.maxFilletRadius_description": "حساب أقصى نصف قطر تقريب ممكن عند زاوية مكونة من قطعتي خط مستقيم تشتركان في نقطة نهاية (C)، بحيث يكون قوس التقريب مماساً لكلا القطعتين ويقع بالكامل ضمنهما.", + "bitbybit.point.maxFilletRadiusHalfLine": "أقصى نصف قطر تقريب (نصف خط)", + "maxFilletRadiusHalfLine": "أقصى نصف قطر تقريب (نصف خط)", + "bitbybit.point.maxFilletRadiusHalfLine_description": "حساب أقصى نصف قطر تقريب ممكن عند زاوية C، بحيث يكون قوس التقريب مماساً لكلا القطعتين (P1-C، P2-C) وتقع نقاط التماس ضمن النصف الأول من كل قطعة (مقاسة من C).", + "bitbybit.point.maxFilletsHalfLine": "أقصى تقريبات (نصف خط)", + "maxFilletsHalfLine": "أقصى تقريبات (نصف خط)", + "bitbybit.point.maxFilletsHalfLine_description": "حساب أقصى نصف قطر تقريب ممكن عند كل زاوية لخط متعدد مكون من سلسلة من النقاط. يتم حساب نصف قطر التقريب لكل زاوية داخلية واختيارياً لزوايا الإغلاق إذا كان الخط المتعدد مغلقاً.", + "checkLastWithFirst": "التحقق من الأخير مع الأول", + "bitbybit.point.safestPointsMaxFilletHalfLine": "أكثر نصف قطر تقريب أماناً للنقاط (نصف خط)", + "safestPointsMaxFilletHalfLine": "أكثر نصف قطر تقريب أماناً للنقاط (نصف خط)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "حساب نصف قطر التقريب الأقصى الأكثر أماناً الذي يمكن تطبيقه بشكل موحد على جميع زوايا مجموعة من النقاط، بناءً على قيد 'نصف الخط'. يتم تحديد ذلك عن طريق إيجاد الحد الأدنى من أنصاف أقطار التقريب القصوى الممكنة المحسوبة لكل زاوية فردية.", + "bitbybit.polyline.maxFilletsHalfLine": "أقصى تقريبات (نصف خط)", + "bitbybit.polyline.maxFilletsHalfLine_description": "حساب أقصى نصف قطر تقريب ممكن وفق قاعدة 'نصف الخط' لكل زاوية في خط متعدد معين. بالنسبة لخط متعدد مغلق، فإنه يشمل الزوايا التي تربط المقطع الأخير بالقطعة الأولى. يستخدم الحساب قيد 'نصف الخط'، مما يعني أن نقاط تماس التقريب يجب أن تقع ضمن النصف الأول من كل قطعة متصلة بالزاوية.", + "bitbybit.polyline.safestFilletRadius": "أكثر نصف قطر تقريب أماناً", + "safestFilletRadius": "أكثر نصف قطر تقريب أماناً", + "bitbybit.polyline.safestFilletRadius_description": "حساب نصف قطر التقريب الأقصى الأكثر أماناً الذي يمكن تطبيقه بشكل موحد على جميع زوايا خط متعدد، بناءً على قيد 'نصف الخط'. يتم تحديد ذلك عن طريق إيجاد الحد الأدنى من أنصاف أقطار التقريب القصوى الممكنة المحسوبة لكل زاوية فردية.", + "flatTop": "قمة مسطحة", + "bitbybit.mesh.meshMeshIntersectionPoints": "نقاط تقاطع شبكة-شبكة", + "meshMeshIntersectionPoints": "نقاط تقاطع شبكة-شبكة", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "حساب نقاط التقاطع لشبكتين.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "سداسيات في شبكة", + "hexagonsInGrid": "سداسيات في شبكة", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "إنشاء هياكل سداسية سلكية باستخدام OpenCascade في شبكة", + "scalePatternWidth": "تغيير قياس عرض النمط", + "scalePatternHeight": "تغيير قياس ارتفاع النمط", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "هياكل تقاطع شبكة-شبكة السلكية", + "meshMeshIntersectionWires": "هياكل تقاطع شبكة-شبكة السلكية", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "تنفيذ عملية تقاطع شبكة-شبكة بين شكلين - يمكن أن يكون لكل شكل دقة تشبيك خاصة به. تقوم هذه الخوارزمية بتقاطع الشبكات وإرجاع الهياكل السلكية للتقاطع، والتي تكون خطوطاً متعددة أو مضلعات.", + "mesh based": "مبني على الشبكة", + "precision1": "الدقة 1", + "precision2": "الدقة 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "نقاط تقاطع شبكة-شبكة", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "تنفيذ عملية تقاطع شبكة-شبكة بين شكلين - يمكن أن يكون لكل شكل دقة تشبيك خاصة به. تقوم هذه الخوارزمية بتقاطع الشبكات وإرجاع نقاط التقاطع.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "تقاطع شبكة-شبكة إلى هياكل سلكية", + "meshMeshIntersectionOfShapesWires": "تقاطع شبكة-شبكة إلى هياكل سلكية", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "تنفيذ عملية تقاطع شبكة-شبكة بين شكل وأشكال أخرى متعددة - يمكن أن يكون لكل شكل دقة تشبيك خاصة به. تقوم هذه الخوارزمية بتقاطع الشبكات وإرجاع الهياكل السلكية للتقاطع، والتي تكون خطوطاً متعددة أو مضلعات.", + "precisionShapes": "دقة الأشكال", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "تقاطع شبكة-شبكة إلى نقاط", + "meshMeshIntersectionOfShapesPoints": "تقاطع شبكة-شبكة إلى نقاط", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "تنفيذ عملية تقاطع شبكة-شبكة بين شكل وأشكال أخرى متعددة - يمكن أن يكون لكل شكل دقة تشبيك خاصة به. تقوم هذه الخوارزمية بتقاطع الشبكات وإرجاع نقاط التقاطع.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "سداسيات في شبكة", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "إنشاء سداسيات OpenCascade في شبكة (أسطح)", + "bitbybit.jscad.toPolygonPoints": "إلى نقاط مضلع", + "toPolygonPoints": "إلى نقاط مضلع", + "bitbybit.jscad.toPolygonPoints_description": "تحويل شكل jscad إلى مجموعة من نقاط المضلع التي تمثل الشبكة", + "conversions": "تحويلات", + "bitbybit.manifold.toPolygonPoints": "إلى نقاط مضلع", + "bitbybit.manifold.toPolygonPoints_description": "تحويل شكل manifold إلى مجموعة من نقاط المضلع التي تمثل الشبكة.", + "flatU": "مسطح U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "تقسيم إلى ثقوب سداسية", + "subdivideToHexagonHoles": "تقسيم إلى ثقوب سداسية", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "يقسم السطح إلى ثقوب سداسية", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "تحويل الخطوط إلى منحنيات NURBS", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "تحويل الخطوط إلى منحنيات NURBS. إرجاع مصفوفة من كائنات verbnurbs Line.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "تحويل الخط إلى منحنى NURBS", + "convertLineToNurbsCurve": "تحويل الخط إلى منحنى NURBS", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "تحويل الخط إلى منحنى NURBS. إرجاع كائن verbnurbs Line.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "تحويل الخط المتعدد إلى منحنى NURBS", + "convertPolylineToNurbsCurve": "تحويل الخط المتعدد إلى منحنى NURBS", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "تحويل خط متعدد إلى منحنى NURBS. إرجاع كائن verbnurbs NurbsCurve.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "تحويل الخطوط المتعددة إلى منحنيات NURBS", + "convertPolylinesToNurbsCurves": "تحويل الخطوط المتعددة إلى منحنيات NURBS", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "تحويل خطوط متعددة إلى منحنيات NURBS. إرجاع كائنات verbnurbs NurbsCurve.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/bn.json b/languages/bn.json index 22c83342..380127e8 100644 --- a/languages/bn.json +++ b/languages/bn.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "আকৃতি থেকে মেশ তৈরি করে", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "বহুভুজ বিন্দু থেকে", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "ত্রিভুজ বর্ণনাকারী বহুভুজ বিন্দুর একটি সেট থেকে একটি ম্যানিফোল্ড তৈরি করুন।", - "traingle": "ত্রিভুজ" + "traingle": "ত্রিভুজ", + "bitbybit.point.stretchPointsDirFromCenter": "কেন্দ্র থেকে দিক বরাবর বিন্দু প্রসারিত করুন", + "stretchPointsDirFromCenter": "কেন্দ্র থেকে দিক বরাবর বিন্দু প্রসারিত করুন", + "bitbybit.point.stretchPointsDirFromCenter_description": "কেন্দ্র বিন্দু, দিক এবং অভিন্ন স্কেল ফ্যাক্টর সরবরাহ করে একাধিক বিন্দু প্রসারিত করুন", + "bitbybit.point.hexGridScaledToFit": "ফিট করার জন্য স্কেল করা হেক্স গ্রিড", + "hexGridScaledToFit": "ফিট করার জন্য স্কেল করা হেক্স গ্রিড", + "bitbybit.point.hexGridScaledToFit_description": "একটি সূক্ষ্ম শীর্ষ ষড়ভুজ গ্রিড তৈরি করে, নির্দিষ্ট মাত্রাগুলির সাথে হুবহু ফিট করার জন্য ষড়ভুজগুলিকে স্কেল করে। প্রতিটি (সম্ভাব্যভাবে স্কেল করা) ষড়ভুজের কেন্দ্র বিন্দু এবং শীর্ষবিন্দু উভয়ই প্রদান করে। ষড়ভুজগুলি প্রথমে কলাম-ভিত্তিক, তারপর সারি-ভিত্তিক সাজানো হয়।", + "nrHexagonsU": "ষড়ভুজের সংখ্যা U", + "nrHexagonsV": "ষড়ভুজের সংখ্যা V", + "extendTop": "উপরে প্রসারিত করুন", + "extendBottom": "নিচে প্রসারিত করুন", + "extendLeft": "বামে প্রসারিত করুন", + "extendRight": "ডানে প্রসারিত করুন", + "centerGrid": "গ্রিড কেন্দ্রীভূত করুন", + "bitbybit.point.sortPoints": "বিন্দু সাজান", + "sortPoints": "বিন্দু সাজান", + "bitbybit.point.sortPoints_description": "আভিধানিকভাবে বিন্দু সাজায় (X, তারপর Y, তারপর Z)", + "bitbybit.line.lineLineIntersection": "রেখা-রেখা ছেদবিন্দু", + "lineLineIntersection": "রেখা-রেখা ছেদবিন্দু", + "bitbybit.line.lineLineIntersection_description": "যদি দুটি রেখা ছেদ করে তবে ছেদ বিন্দুটি প্রদান করুন", + "line1": "রেখা ১", + "line2": "রেখা ২", + "checkSegmentsOnly": "শুধুমাত্র সেগমেন্ট পরীক্ষা করুন", + "bitbybit.polyline.polylineToLines": "পলিলাইন থেকে রেখা", + "polylineToLines": "পলিলাইন থেকে রেখা", + "bitbybit.polyline.polylineToLines_description": "পলিলাইন থেকে রেখা তৈরি করুন", + "bitbybit.polyline.polylineToSegments": "পলিলাইন থেকে সেগমেন্ট", + "polylineToSegments": "পলিলাইন থেকে সেগমেন্ট", + "bitbybit.polyline.polylineToSegments_description": "পলিলাইন থেকে সেগমেন্ট তৈরি করুন", + "bitbybit.polyline.polylineSelfIntersection": "পলিলাইন স্ব-ছেদ", + "polylineSelfIntersection": "পলিলাইন স্ব-ছেদ", + "bitbybit.polyline.polylineSelfIntersection_description": "পলিলাইনের স্ব-ছেদ বিন্দুগুলি খুঁজে বের করে", + "bitbybit.polyline.twoPolylineIntersection": "দুটি পলিলাইন ছেদ", + "twoPolylineIntersection": "দুটি পলিলাইন ছেদ", + "bitbybit.polyline.twoPolylineIntersection_description": "দুটি পলিলাইনের মধ্যে ছেদ বিন্দুগুলি খুঁজে বের করে।", + "polyline1": "পলিলাইন ১", + "polyline2": "পলিলাইন ২", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "ষড়ভুজ তারে উপবিভক্ত করুন", + "subdivideToHexagonWires": "ষড়ভুজ তারে উপবিভক্ত করুন", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "একটি মুখকে ষড়ভুজ তারে উপবিভক্ত করে", + "extendUUp": "U উপরে প্রসারিত করুন", + "extendUBottom": "U নিচে প্রসারিত করুন", + "extendVUp": "V উপরে প্রসারিত করুন", + "extendVBottom": "V নিচে প্রসারিত করুন", + "nrHexagonsInHeight": "উচ্চতা/প্রস্থে ষড়ভুজের সংখ্যা", + "nrHexagonsInWidth": "প্রস্থে ষড়ভুজের সংখ্যা", + "bitbybit.vector.length": "ভেক্টরের দৈর্ঘ্য", + "bitbybit.vector.length_description": "ভেক্টরের দৈর্ঘ্য গণনা করে", + "bitbybit.point.maxFilletRadius": "সর্বোচ্চ ফিলেট ব্যাসার্ধ", + "maxFilletRadius": "সর্বোচ্চ ফিলেট ব্যাসার্ধ", + "bitbybit.point.maxFilletRadius_description": "দুটি রেখাংশ দ্বারা গঠিত একটি কোণে (যা একটি সাধারণ শেষবিন্দু C শেয়ার করে) সর্বাধিক সম্ভাব্য ফিলেট ব্যাসার্ধ গণনা করে, যাতে ফিলেট চাপ উভয় রেখাংশের স্পর্শক হয় এবং সম্পূর্ণরূপে তাদের মধ্যে থাকে।", + "bitbybit.point.maxFilletRadiusHalfLine": "সর্বোচ্চ ফিলেট ব্যাসার্ধ অর্ধেক রেখা", + "maxFilletRadiusHalfLine": "সর্বোচ্চ ফিলেট ব্যাসার্ধ অর্ধেক রেখা", + "bitbybit.point.maxFilletRadiusHalfLine_description": "কোণ C তে সর্বাধিক সম্ভাব্য ফিলেট ব্যাসার্ধ গণনা করে, যাতে ফিলেট চাপ উভয় রেখাংশ (P1-C, P2-C) এর স্পর্শক হয় এবং স্পর্শ বিন্দুগুলি প্রতিটি রেখাংশের প্রথম অর্ধেকের মধ্যে (C থেকে পরিমাপ করা) অবস্থিত হয়।", + "bitbybit.point.maxFilletsHalfLine": "সর্বোচ্চ ফিলেট অর্ধেক রেখা", + "maxFilletsHalfLine": "সর্বোচ্চ ফিলেট অর্ধেক রেখা", + "bitbybit.point.maxFilletsHalfLine_description": "বিন্দুর একটি সিরিজ দ্বারা গঠিত একটি পলিলাইনের প্রতিটি কোণে সর্বাধিক সম্ভাব্য ফিলেট ব্যাসার্ধ গণনা করে। ফিলেট ব্যাসার্ধ প্রতিটি অভ্যন্তরীণ কোণের জন্য এবং ঐচ্ছিকভাবে ক্লোজিং কোণগুলির জন্য গণনা করা হয় যদি পলিলাইনটি বন্ধ থাকে।", + "checkLastWithFirst": "শেষের সাথে প্রথমটি পরীক্ষা করুন", + "bitbybit.point.safestPointsMaxFilletHalfLine": "নিরাপদতম বিন্দু সর্বোচ্চ ফিলেট অর্ধেক রেখা", + "safestPointsMaxFilletHalfLine": "নিরাপদতম বিন্দু সর্বোচ্চ ফিলেট অর্ধেক রেখা", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "'অর্ধ-রেখা' সীমাবদ্ধতার উপর ভিত্তি করে, বিন্দু সংগ্রহের সমস্ত কোণে অভিন্নভাবে প্রয়োগ করা যেতে পারে এমন একক নিরাপদতম সর্বোচ্চ ফিলেট ব্যাসার্ধ গণনা করে। এটি প্রতিটি স্বতন্ত্র কোণের জন্য গণনা করা সর্বাধিক সম্ভাব্য ফিলেট ব্যাসার্ধের সর্বনিম্নটি খুঁজে বের করে নির্ধারিত হয়।", + "bitbybit.polyline.maxFilletsHalfLine": "সর্বোচ্চ ফিলেট অর্ধেক রেখা", + "bitbybit.polyline.maxFilletsHalfLine_description": "প্রদত্ত পলিলাইনের প্রতিটি কোণের জন্য সর্বাধিক সম্ভাব্য হাফ-লাইন ফিলেট ব্যাসার্ধ গণনা করে। একটি বন্ধ পলিলাইনের জন্য, এটি শেষ সেগমেন্টকে প্রথমটির সাথে সংযোগকারী কোণগুলি অন্তর্ভুক্ত করে। গণনাটি 'হাফ-লাইন' সীমাবদ্ধতা ব্যবহার করে, যার অর্থ ফিলেটের স্পর্শক বিন্দুগুলি কোণের সাথে সংযুক্ত প্রতিটি সেগমেন্টের প্রথম অর্ধেকের মধ্যে থাকতে হবে।", + "bitbybit.polyline.safestFilletRadius": "নিরাপদতম ফিলেট ব্যাসার্ধ", + "safestFilletRadius": "নিরাপদতম ফিলেট ব্যাসার্ধ", + "bitbybit.polyline.safestFilletRadius_description": "'অর্ধ-রেখা' সীমাবদ্ধতার উপর ভিত্তি করে, পলিলাইনের সমস্ত কোণে অভিন্নভাবে প্রয়োগ করা যেতে পারে এমন একক নিরাপদতম সর্বোচ্চ ফিলেট ব্যাসার্ধ গণনা করে। এটি প্রতিটি স্বতন্ত্র কোণের জন্য গণনা করা সর্বাধিক সম্ভাব্য ফিলেট ব্যাসার্ধের সর্বনিম্নটি খুঁজে বের করে নির্ধারিত হয়।", + "flatTop": "সমতল শীর্ষ", + "bitbybit.mesh.meshMeshIntersectionPoints": "মেশ-মেশ ছেদ বিন্দু", + "meshMeshIntersectionPoints": "মেশ-মেশ ছেদ বিন্দু", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "দুটি মেশের ছেদ বিন্দু গণনা করে।", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "গ্রিডে ষড়ভুজ", + "hexagonsInGrid": "গ্রিডে ষড়ভুজ", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "গ্রিডে OpenCascade ষড়ভুজ তার তৈরি করে", + "scalePatternWidth": "প্যাটার্নের প্রস্থ স্কেল করুন", + "scalePatternHeight": "প্যাটার্নের উচ্চতা স্কেল করুন", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "মেশ-মেশ ছেদ তার", + "meshMeshIntersectionWires": "মেশ-মেশ ছেদ তার", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "দুটি আকারের মধ্যে মেশ-মেশ ছেদ অপারেশন করে - উভয় আকারের নিজস্ব মেশিং নির্ভুলতা থাকতে পারে। এই অ্যালগরিদম মেশগুলিকে ছেদ করে এবং ছেদের তারগুলি ফেরত দেয়, যা পলিলাইন বা বহুভুজ।", + "mesh based": "মেশ ভিত্তিক", + "precision1": "নির্ভুলতা ১", + "precision2": "নির্ভুলতা ২", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "মেশ-মেশ ছেদ বিন্দু", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "দুটি আকারের মধ্যে মেশ-মেশ ছেদ অপারেশন করে - উভয় আকারের নিজস্ব মেশিং নির্ভুলতা থাকতে পারে। এই অ্যালগরিদম মেশগুলিকে ছেদ করে এবং ছেদের বিন্দুগুলি ফেরত দেয়।", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "মেশ-মেশ ছেদ থেকে তার", + "meshMeshIntersectionOfShapesWires": "মেশ-মেশ ছেদ থেকে তার", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "আকার এবং একাধিক অন্যান্য আকারের মধ্যে মেশ-মেশ ছেদ অপারেশন করে - সমস্ত আকারের নিজস্ব মেশিং নির্ভুলতা থাকতে পারে। এই অ্যালগরিদম মেশগুলিকে ছেদ করে এবং ছেদের তারগুলি ফেরত দেয়, যা পলিলাইন বা বহুভুজ।", + "precisionShapes": "আকারের নির্ভুলতা", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "মেশ-মেশ ছেদ থেকে বিন্দু", + "meshMeshIntersectionOfShapesPoints": "মেশ-মেশ ছেদ থেকে বিন্দু", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "আকার এবং একাধিক অন্যান্য আকারের মধ্যে মেশ-মেশ ছেদ অপারেশন করে - সমস্ত আকারের নিজস্ব মেশিং নির্ভুলতা থাকতে পারে। এই অ্যালগরিদম মেশগুলিকে ছেদ করে এবং ছেদের বিন্দুগুলি ফেরত দেয়।", + "bitbybit.occt.shapes.face.hexagonsInGrid": "গ্রিডে ষড়ভুজ", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "গ্রিডে OpenCascade ষড়ভুজ তৈরি করে (মুখ)", + "bitbybit.jscad.toPolygonPoints": "বহুভুজ বিন্দুতে", + "toPolygonPoints": "বহুভুজ বিন্দুতে", + "bitbybit.jscad.toPolygonPoints_description": "jscad আকারকে মেশের প্রতিনিধিত্বকারী বহুভুজ বিন্দুর সংগ্রহে পরিণত করে", + "conversions": "রূপান্তর", + "bitbybit.manifold.toPolygonPoints": "বহুভুজ বিন্দুতে", + "bitbybit.manifold.toPolygonPoints_description": "manifold আকারকে মেশের প্রতিনিধিত্বকারী বহুভুজ বিন্দুর সংগ্রহে পরিণত করে।", + "flatU": "সমতল U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "ষড়ভুজ গর্তে উপবিভক্ত করুন", + "subdivideToHexagonHoles": "ষড়ভুজ গর্তে উপবিভক্ত করুন", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "একটি মুখকে ষড়ভুজ গর্তে উপবিভক্ত করে", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "রেখাগুলোকে NURBS বক্ররেখায় রূপান্তর করুন", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "রেখাগুলোকে NURBS বক্ররেখায় রূপান্তর করে। verbnurbs Line অবজেক্টের একটি অ্যারে প্রদান করে।", + "bitbybit.verb.curve.convertLineToNurbsCurve": "রেখাকে NURBS বক্ররেখায় রূপান্তর করুন", + "convertLineToNurbsCurve": "রেখাকে NURBS বক্ররেখায় রূপান্তর করুন", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "রেখাকে NURBS বক্ররেখায় রূপান্তর করে। verbnurbs Line অবজেক্ট প্রদান করে।", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "পলিলাইনকে NURBS বক্ররেখায় রূপান্তর করুন", + "convertPolylineToNurbsCurve": "পলিলাইনকে NURBS বক্ররেখায় রূপান্তর করুন", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "একটি পলিলাইনকে NURBS বক্ররেখায় রূপান্তর করে। verbnurbs NurbsCurve অবজেক্ট প্রদান করে।", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "পলিলাইনগুলোকে NURBS বক্ররেখায় রূপান্তর করুন", + "convertPolylinesToNurbsCurves": "পলিলাইনগুলোকে NURBS বক্ররেখায় রূপান্তর করুন", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "পলিলাইনগুলোকে NURBS বক্ররেখায় রূপান্তর করে। verbnurbs NurbsCurve অবজেক্টগুলো প্রদান করে।", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/de.json b/languages/de.json index 511b1ced..746236fd 100644 --- a/languages/de.json +++ b/languages/de.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Erstellt ein Netz aus der Form", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "aus Polygonpunkten", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Erstellen Sie einen Manifold aus einer Reihe von Polygonpunkten, die Dreiecke beschreiben.", - "traingle": "Dreieck" + "traingle": "Dreieck", + "bitbybit.point.stretchPointsDirFromCenter": "Punkte in Richtung vom Zentrum strecken", + "stretchPointsDirFromCenter": "Punkte in Richtung vom Zentrum strecken", + "bitbybit.point.stretchPointsDirFromCenter_description": "Streckt mehrere Punkte unter Angabe von Mittelpunkt, Richtung und einheitlichem Skalierungsfaktor", + "bitbybit.point.hexGridScaledToFit": "Angepasstes Sechseckraster", + "hexGridScaledToFit": "Angepasstes Sechseckraster", + "bitbybit.point.hexGridScaledToFit_description": "Erstellt ein Sechseckraster mit spitzer Oberseite, wobei die Sechsecke so skaliert werden, dass sie genau den angegebenen Abmessungen entsprechen. Gibt sowohl die Mittelpunkte als auch die Eckpunkte jedes (potenziell skalierten) Sechsecks zurück. Sechsecke werden zuerst nach Spalten, dann nach Zeilen sortiert.", + "nrHexagonsU": "Anz. Sechsecke U", + "nrHexagonsV": "Anz. Sechsecke V", + "extendTop": "oben erweitern", + "extendBottom": "unten erweitern", + "extendLeft": "links erweitern", + "extendRight": "rechts erweitern", + "centerGrid": "Raster zentrieren", + "bitbybit.point.sortPoints": "Punkte sortieren", + "sortPoints": "Punkte sortieren", + "bitbybit.point.sortPoints_description": "Sortiert Punkte lexikographisch (X, dann Y, dann Z)", + "bitbybit.line.lineLineIntersection": "Schnittpunkt Linie-Linie", + "lineLineIntersection": "Schnittpunkt Linie-Linie", + "bitbybit.line.lineLineIntersection_description": "Wenn sich zwei Linien schneiden, gibt den Schnittpunkt zurück", + "line1": "Linie 1", + "line2": "Linie 2", + "checkSegmentsOnly": "nur Segmente prüfen", + "bitbybit.polyline.polylineToLines": "Polylinie zu Linien", + "polylineToLines": "Polylinie zu Linien", + "bitbybit.polyline.polylineToLines_description": "Erstellt die Linien aus der Polylinie", + "bitbybit.polyline.polylineToSegments": "Polylinie zu Segmenten", + "polylineToSegments": "Polylinie zu Segmenten", + "bitbybit.polyline.polylineToSegments_description": "Erstellt die Segmente aus der Polylinie", + "bitbybit.polyline.polylineSelfIntersection": "Selbstschnitt Polylinie", + "polylineSelfIntersection": "Selbstschnitt Polylinie", + "bitbybit.polyline.polylineSelfIntersection_description": "Findet die Selbstschnittpunkte der Polylinie", + "bitbybit.polyline.twoPolylineIntersection": "Schnitt zweier Polylinien", + "twoPolylineIntersection": "Schnitt zweier Polylinien", + "bitbybit.polyline.twoPolylineIntersection_description": "Findet die Schnittpunkte zwischen zwei Polylinien.", + "polyline1": "Polylinie 1", + "polyline2": "Polylinie 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "in Sechseck-Drähte unterteilen", + "subdivideToHexagonWires": "in Sechseck-Drähte unterteilen", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Unterteilt eine Fläche in Sechseck-Drähte", + "extendUUp": "U oben erweitern", + "extendUBottom": "U unten erweitern", + "extendVUp": "V oben erweitern", + "extendVBottom": "V unten erweitern", + "nrHexagonsInHeight": "Anz. Sechsecke Höhe", + "nrHexagonsInWidth": "Anz. Sechsecke Breite", + "bitbybit.vector.length": "Vektorlänge", + "bitbybit.vector.length_description": "Berechnet die Länge des Vektors", + "bitbybit.point.maxFilletRadius": "max. Verrundungsradius", + "maxFilletRadius": "max. Verrundungsradius", + "bitbybit.point.maxFilletRadius_description": "Berechnet den maximal möglichen Verrundungsradius an einer Ecke, die von zwei Liniensegmenten mit gemeinsamem Endpunkt (C) gebildet wird, sodass der Verrundungsbogen tangential zu beiden Segmenten verläuft und vollständig innerhalb dieser liegt.", + "bitbybit.point.maxFilletRadiusHalfLine": "max. Verrundungsradius halbe Linie", + "maxFilletRadiusHalfLine": "max. Verrundungsradius halbe Linie", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Berechnet den maximal möglichen Verrundungsradius an einer Ecke C, sodass der Verrundungsbogen tangential zu beiden Segmenten (P1-C, P2-C) verläuft und die Tangentialpunkte innerhalb der ersten Hälfte jedes Segments (gemessen von C) liegen.", + "bitbybit.point.maxFilletsHalfLine": "max. Verrundungen halbe Linie", + "maxFilletsHalfLine": "max. Verrundungen halbe Linie", + "bitbybit.point.maxFilletsHalfLine_description": "Berechnet den maximal möglichen Verrundungsradius an jeder Ecke einer Polylinie, die durch eine Reihe von Punkten gebildet wird. Der Verrundungsradius wird für jede innere Ecke und optional für die Schließecken berechnet, wenn die Polylinie geschlossen ist.", + "checkLastWithFirst": "letztes mit erstem prüfen", + "bitbybit.point.safestPointsMaxFilletHalfLine": "sicherster max. Verrundungsradius (Punkte, halbe Linie)", + "safestPointsMaxFilletHalfLine": "sicherster max. Verrundungsradius (Punkte, halbe Linie)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Berechnet den einzelnen sichersten maximalen Verrundungsradius, der basierend auf der 'halbe Linie'-Beschränkung einheitlich auf alle Ecken einer Punktsammlung angewendet werden kann. Dieser wird durch Ermittlung des Minimums der für jede einzelne Ecke berechneten maximal möglichen Verrundungsradien bestimmt.", + "bitbybit.polyline.maxFilletsHalfLine": "max. Verrundungen halbe Linie", + "bitbybit.polyline.maxFilletsHalfLine_description": "Berechnet den maximal möglichen 'halbe Linie'-Verrundungsradius für jede Ecke einer gegebenen Polylinie. Bei einer geschlossenen Polylinie schließt dies die Ecken ein, die das letzte Segment mit dem ersten verbinden. Die Berechnung verwendet die 'halbe Linie'-Beschränkung, was bedeutet, dass die Tangentialpunkte der Verrundung innerhalb der ersten Hälfte jedes mit der Ecke verbundenen Segments liegen müssen.", + "bitbybit.polyline.safestFilletRadius": "sicherster Verrundungsradius", + "safestFilletRadius": "sicherster Verrundungsradius", + "bitbybit.polyline.safestFilletRadius_description": "Berechnet den einzelnen sichersten maximalen Verrundungsradius, der basierend auf der 'halbe Linie'-Beschränkung einheitlich auf alle Ecken einer Polylinie angewendet werden kann. Dieser wird durch Ermittlung des Minimums der für jede einzelne Ecke berechneten maximal möglichen Verrundungsradien bestimmt.", + "flatTop": "flache Oberseite", + "bitbybit.mesh.meshMeshIntersectionPoints": "Schnittpunkte Netz-Netz", + "meshMeshIntersectionPoints": "Schnittpunkte Netz-Netz", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Berechnet die Schnittpunkte zweier Netze.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "Sechsecke im Raster", + "hexagonsInGrid": "Sechsecke im Raster", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Erstellt OpenCascade Sechseck-Drähte im Raster", + "scalePatternWidth": "Musterbreite skalieren", + "scalePatternHeight": "Musterhöhe skalieren", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "Schnittdrähte Netz-Netz", + "meshMeshIntersectionWires": "Schnittdrähte Netz-Netz", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Führt eine Netz-Netz-Schnittoperation zwischen zwei Formen durch - beide Formen können ihre eigene Vernetzungspräzision haben. Dieser Algorithmus schneidet die Netze und gibt die Drähte des Schnitts zurück, die Polylinien oder Polygone sind.", + "mesh based": "Netzbasiert", + "precision1": "Präzision 1", + "precision2": "Präzision 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "Schnittpunkte Netz-Netz", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Führt eine Netz-Netz-Schnittoperation zwischen zwei Formen durch - beide Formen können ihre eigene Vernetzungspräzision haben. Dieser Algorithmus schneidet die Netze und gibt die Punkte des Schnitts zurück.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "Netz-Netz-Schnitt zu Drähten", + "meshMeshIntersectionOfShapesWires": "Netz-Netz-Schnitt zu Drähten", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Führt eine Netz-Netz-Schnittoperation zwischen der Form und mehreren anderen Formen durch - alle Formen können ihre eigene Vernetzungspräzision haben. Dieser Algorithmus schneidet die Netze und gibt die Drähte des Schnitts zurück, die Polylinien oder Polygone sind.", + "precisionShapes": "Präzision Formen", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "Netz-Netz-Schnitt zu Punkten", + "meshMeshIntersectionOfShapesPoints": "Netz-Netz-Schnitt zu Punkten", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Führt eine Netz-Netz-Schnittoperation zwischen der Form und mehreren anderen Formen durch - alle Formen können ihre eigene Vernetzungspräzision haben. Dieser Algorithmus schneidet die Netze und gibt die Punkte des Schnitts zurück.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "Sechsecke im Raster", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Erstellt OpenCascade Sechsecke im Raster (als Flächen)", + "bitbybit.jscad.toPolygonPoints": "zu Polygonpunkten", + "toPolygonPoints": "zu Polygonpunkten", + "bitbybit.jscad.toPolygonPoints_description": "Wandelt eine jscad-Form in eine Sammlung von Polygonpunkten um, die das Netz darstellen", + "conversions": "Umwandlungen", + "bitbybit.manifold.toPolygonPoints": "zu Polygonpunkten", + "bitbybit.manifold.toPolygonPoints_description": "Wandelt eine Manifold-Form in eine Sammlung von Polygonpunkten um, die das Netz darstellen.", + "flatU": "flaches U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "in Sechseck-Löcher unterteilen", + "subdivideToHexagonHoles": "in Sechseck-Löcher unterteilen", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Unterteilt eine Fläche in Sechseck-Löcher", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "Linien in NURBS-Kurven konvertieren", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Konvertiert Linien in NURBS-Kurven. Gibt ein Array von verbnurbs Line-Objekten zurück.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "Linie in NURBS-Kurve konvertieren", + "convertLineToNurbsCurve": "Linie in NURBS-Kurve konvertieren", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Konvertiert eine Linie in eine NURBS-Kurve. Gibt das verbnurbs Line-Objekt zurück.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "Polylinie in NURBS-Kurve konvertieren", + "convertPolylineToNurbsCurve": "Polylinie in NURBS-Kurve konvertieren", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Konvertiert eine Polylinie in eine NURBS-Kurve. Gibt das verbnurbs NurbsCurve-Objekt zurück.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "Polylinien in NURBS-Kurven konvertieren", + "convertPolylinesToNurbsCurves": "Polylinien in NURBS-Kurven konvertieren", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Konvertiert Polylinien in NURBS-Kurven. Gibt die verbnurbs NurbsCurve-Objekte zurück.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/en.json b/languages/en.json index 243d3ef7..0d3ff6de 100644 --- a/languages/en.json +++ b/languages/en.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Creates mesh from the shape", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "from polygon points", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Create a Manifold from a set of polygon points describing triangles.", - "traingle": "traingle" + "traingle": "traingle", + "bitbybit.point.stretchPointsDirFromCenter": "stretch points dir from center", + "stretchPointsDirFromCenter": "stretch points dir from center", + "bitbybit.point.stretchPointsDirFromCenter_description": "Stretch multiple points by providing center point, direction and uniform scale factor", + "bitbybit.point.hexGridScaledToFit": "hex grid scaled to fit", + "hexGridScaledToFit": "hex grid scaled to fit", + "bitbybit.point.hexGridScaledToFit_description": "Creates a pointy top hexagon grid, scaling hexagons to fit specified dimensions exactly. Returns both center points and the vertices of each (potentially scaled) hexagon. Hexagons are ordered column-first, then row-first.", + "nrHexagonsU": "nr hexagons u", + "nrHexagonsV": "nr hexagons v", + "extendTop": "extend top", + "extendBottom": "extend bottom", + "extendLeft": "extend left", + "extendRight": "extend right", + "centerGrid": "center grid", + "bitbybit.point.sortPoints": "sort points", + "sortPoints": "sort points", + "bitbybit.point.sortPoints_description": "Sorts points lexicographically (X, then Y, then Z)", + "bitbybit.line.lineLineIntersection": "line line intersection", + "lineLineIntersection": "line line intersection", + "bitbybit.line.lineLineIntersection_description": "If two lines intersect return the intersection point", + "line1": "line 1", + "line2": "line 2", + "checkSegmentsOnly": "check segments only", + "bitbybit.polyline.polylineToLines": "polyline to lines", + "polylineToLines": "polyline to lines", + "bitbybit.polyline.polylineToLines_description": "Create the lines from the polyline", + "bitbybit.polyline.polylineToSegments": "polyline to segments", + "polylineToSegments": "polyline to segments", + "bitbybit.polyline.polylineToSegments_description": "Create the segments from the polyline", + "bitbybit.polyline.polylineSelfIntersection": "polyline self intersection", + "polylineSelfIntersection": "polyline self intersection", + "bitbybit.polyline.polylineSelfIntersection_description": "Finds the points of self intersection of the polyline", + "bitbybit.polyline.twoPolylineIntersection": "two polyline intersection", + "twoPolylineIntersection": "two polyline intersection", + "bitbybit.polyline.twoPolylineIntersection_description": "Finds the intersection points between two polylines.", + "polyline1": "polyline 1", + "polyline2": "polyline 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "subdivide to hexagon wires", + "subdivideToHexagonWires": "subdivide to hexagon wires", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Subdivides a face to hexagon wires", + "extendUUp": "extend u up", + "extendUBottom": "extend u bottom", + "extendVUp": "extend v up", + "extendVBottom": "extend v bottom", + "nrHexagonsInHeight": "nr hexagons in height", + "nrHexagonsInWidth": "nr hexagons in width", + "bitbybit.vector.length": "vector length", + "bitbybit.vector.length_description": "Computes the length of the vector", + "bitbybit.point.maxFilletRadius": "max fillet radius", + "maxFilletRadius": "max fillet radius", + "bitbybit.point.maxFilletRadius_description": "Calculates the maximum possible fillet radius at a corner formed by two line segments sharing an endpoint (C), such that the fillet arc is tangent to both segments and lies entirely within them.", + "bitbybit.point.maxFilletRadiusHalfLine": "max fillet radius half line", + "maxFilletRadiusHalfLine": "max fillet radius half line", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Calculates the maximum possible fillet radius at a corner C, such that the fillet arc is tangent to both segments (P1-C, P2-C) and the tangent points lie within the first half of each segment (measured from C).", + "bitbybit.point.maxFilletsHalfLine": "max fillets half line", + "maxFilletsHalfLine": "max fillets half line", + "bitbybit.point.maxFilletsHalfLine_description": "Calculates the maximum possible fillet radius at each corner of a polyline formed by formed by a series of points. The fillet radius is calculated for each internal corner and optionally for the closing corners if the polyline is closed.", + "checkLastWithFirst": "check last with first", + "bitbybit.point.safestPointsMaxFilletHalfLine": "safest points max fillet half line", + "safestPointsMaxFilletHalfLine": "safest points max fillet half line", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Calculates the single safest maximum fillet radius that can be applied uniformly to all corners of collection of points, based on the 'half-line' constraint. This is determined by finding the minimum of the maximum possible fillet radii calculated for each individual corner.", + "bitbybit.polyline.maxFilletsHalfLine": "max fillets half line", + "bitbybit.polyline.maxFilletsHalfLine_description": "Calculates the maximum possible half-line fillet radius for each corner of a given polyline. For a closed polyline, it includes the corners connecting the last segment back to the first. The calculation uses the 'half-line' constraint, meaning the fillet's tangent points must lie within the first half of each segment connected to the corner.", + "bitbybit.polyline.safestFilletRadius": "safest fillet radius", + "safestFilletRadius": "safest fillet radius", + "bitbybit.polyline.safestFilletRadius_description": "Calculates the single safest maximum fillet radius that can be applied uniformly to all corners of a polyline, based on the 'half-line' constraint. This is determined by finding the minimum of the maximum possible fillet radii calculated for each individual corner.", + "flatTop": "flat top", + "bitbybit.mesh.meshMeshIntersectionPoints": "mesh mesh intersection points", + "meshMeshIntersectionPoints": "mesh mesh intersection points", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Computes the intersection points of two meshes.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "hexagons in grid", + "hexagonsInGrid": "hexagons in grid", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Creates OpenCascade hexagon wires in grid", + "scalePatternWidth": "scale pattern width", + "scalePatternHeight": "scale pattern height", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "mesh mesh intersection wires", + "meshMeshIntersectionWires": "mesh mesh intersection wires", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Does mesh mesh intersection operation between two shapes - both shapes can have their own meshing precision. This algorithm intersects the meshes and returns the wires of the intersection, which are polylines or polygons.", + "mesh based": "mesh based", + "precision1": "precision 1", + "precision2": "precision 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "mesh mesh intersection points", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Does mesh mesh intersection operation between two shapes - both shapes can have their own meshing precision. This algorithm intersects the meshes and returns the points of the intersection, which are polylines or polygons.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "mesh mesh intersection to wires", + "meshMeshIntersectionOfShapesWires": "mesh mesh intersection to wires", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Does mesh mesh intersection operation between the shape and multiple other shapes - all shapes can have their own meshing precision. This algorithm intersects the meshes and returns the wires of the intersection, which are polylines or polygons.", + "precisionShapes": "precision shapes", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "mesh mesh intersection to points", + "meshMeshIntersectionOfShapesPoints": "mesh mesh intersection to points", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Does mesh mesh intersection operation between the shape and multiple other shapes - all shapes can have their own meshing precision. This algorithm intersects the meshes and returns the points of the intersection.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "hexagons in grid", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Creates OpenCascade hexagons in grid", + "bitbybit.jscad.toPolygonPoints": "to polygon points", + "toPolygonPoints": "to polygon points", + "bitbybit.jscad.toPolygonPoints_description": "Turns jscad shape into a collection of polygon points representing the mesh", + "conversions": "conversions", + "bitbybit.manifold.toPolygonPoints": "to polygon points", + "bitbybit.manifold.toPolygonPoints_description": "Turns manifold shape into a collection of polygon points representing the mesh.", + "flatU": "flat u", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "subdivide to hexagon holes", + "subdivideToHexagonHoles": "subdivide to hexagon holes", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Subdivides a face to hexagon holes", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "convert lines to nurbs curves", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Converts lines to NURBS curves Returns array of the verbnurbs Line objects", + "bitbybit.verb.curve.convertLineToNurbsCurve": "convert line to nurbs curve", + "convertLineToNurbsCurve": "convert line to nurbs curve", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Converts line to NURBS curve Returns the verbnurbs Line object", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "convert polyline to nurbs curve", + "convertPolylineToNurbsCurve": "convert polyline to nurbs curve", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Converts a polyline to a NURBS curve Returns the verbnurbs NurbsCurve object", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "convert polylines to nurbs curves", + "convertPolylinesToNurbsCurves": "convert polylines to nurbs curves", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Converts a polylines to a NURBS curves Returns the verbnurbs NurbsCurve objects", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/es.json b/languages/es.json index 171383c8..0f2234ce 100644 --- a/languages/es.json +++ b/languages/es.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Crea una malla a partir de la forma", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "desde puntos de polígono", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Crear un Manifold a partir de un conjunto de puntos de polígono que describen triángulos.", - "traingle": "triángulo" + "traingle": "triángulo", + "bitbybit.point.stretchPointsDirFromCenter": "estirar puntos en dirección desde el centro", + "stretchPointsDirFromCenter": "estirar puntos en dirección desde el centro", + "bitbybit.point.stretchPointsDirFromCenter_description": "Estira múltiples puntos proporcionando un punto central, dirección y factor de escala uniforme", + "bitbybit.point.hexGridScaledToFit": "rejilla hexagonal escalada para ajustar", + "hexGridScaledToFit": "rejilla hexagonal escalada para ajustar", + "bitbybit.point.hexGridScaledToFit_description": "Crea una rejilla hexagonal de punta superior, escalando los hexágonos para ajustarse exactamente a las dimensiones especificadas. Devuelve tanto los puntos centrales como los vértices de cada hexágono (potencialmente escalado). Los hexágonos se ordenan primero por columna y luego por fila.", + "nrHexagonsU": "nº hexágonos U", + "nrHexagonsV": "nº hexágonos V", + "extendTop": "extender arriba", + "extendBottom": "extender abajo", + "extendLeft": "extender izquierda", + "extendRight": "extender derecha", + "centerGrid": "centrar rejilla", + "bitbybit.point.sortPoints": "ordenar puntos", + "sortPoints": "ordenar puntos", + "bitbybit.point.sortPoints_description": "Ordena los puntos lexicográficamente (X, luego Y, luego Z)", + "bitbybit.line.lineLineIntersection": "intersección línea-línea", + "lineLineIntersection": "intersección línea-línea", + "bitbybit.line.lineLineIntersection_description": "Si dos líneas se intersecan, devuelve el punto de intersección", + "line1": "línea 1", + "line2": "línea 2", + "checkSegmentsOnly": "comprobar solo segmentos", + "bitbybit.polyline.polylineToLines": "polilínea a líneas", + "polylineToLines": "polilínea a líneas", + "bitbybit.polyline.polylineToLines_description": "Crea las líneas a partir de la polilínea", + "bitbybit.polyline.polylineToSegments": "polilínea a segmentos", + "polylineToSegments": "polilínea a segmentos", + "bitbybit.polyline.polylineToSegments_description": "Crea los segmentos a partir de la polilínea", + "bitbybit.polyline.polylineSelfIntersection": "autointersección de polilínea", + "polylineSelfIntersection": "autointersección de polilínea", + "bitbybit.polyline.polylineSelfIntersection_description": "Encuentra los puntos de autointersección de la polilínea", + "bitbybit.polyline.twoPolylineIntersection": "intersección de dos polilíneas", + "twoPolylineIntersection": "intersección de dos polilíneas", + "bitbybit.polyline.twoPolylineIntersection_description": "Encuentra los puntos de intersección entre dos polilíneas.", + "polyline1": "polilínea 1", + "polyline2": "polilínea 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "subdividir en alambres hexagonales", + "subdivideToHexagonWires": "subdividir en alambres hexagonales", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Subdivide una cara en alambres hexagonales", + "extendUUp": "extender U arriba", + "extendUBottom": "extender U abajo", + "extendVUp": "extender V arriba", + "extendVBottom": "extender V abajo", + "nrHexagonsInHeight": "nº hexágonos en altura", + "nrHexagonsInWidth": "nº hexágonos en anchura", + "bitbybit.vector.length": "longitud del vector", + "bitbybit.vector.length_description": "Calcula la longitud del vector", + "bitbybit.point.maxFilletRadius": "radio máximo de redondeo", + "maxFilletRadius": "radio máximo de redondeo", + "bitbybit.point.maxFilletRadius_description": "Calcula el radio máximo de redondeo posible en una esquina formada por dos segmentos de línea que comparten un punto final (C), de modo que el arco de redondeo sea tangente a ambos segmentos y se encuentre completamente dentro de ellos.", + "bitbybit.point.maxFilletRadiusHalfLine": "radio máximo de redondeo media línea", + "maxFilletRadiusHalfLine": "radio máximo de redondeo media línea", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Calcula el radio máximo de redondeo posible en una esquina C, de modo que el arco de redondeo sea tangente a ambos segmentos (P1-C, P2-C) y los puntos tangentes se encuentren dentro de la primera mitad de cada segmento (medido desde C).", + "bitbybit.point.maxFilletsHalfLine": "redondeos máximos media línea", + "maxFilletsHalfLine": "redondeos máximos media línea", + "bitbybit.point.maxFilletsHalfLine_description": "Calcula el radio máximo de redondeo posible en cada esquina de una polilínea formada por una serie de puntos. El radio de redondeo se calcula para cada esquina interna y opcionalmente para las esquinas de cierre si la polilínea está cerrada.", + "checkLastWithFirst": "comprobar último con primero", + "bitbybit.point.safestPointsMaxFilletHalfLine": "radio máximo de redondeo más seguro (puntos, media línea)", + "safestPointsMaxFilletHalfLine": "radio máximo de redondeo más seguro (puntos, media línea)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Calcula el único radio máximo de redondeo más seguro que se puede aplicar uniformemente a todas las esquinas de una colección de puntos, basado en la restricción de 'media línea'. Esto se determina encontrando el mínimo de los radios máximos de redondeo posibles calculados para cada esquina individual.", + "bitbybit.polyline.maxFilletsHalfLine": "redondeos máximos media línea", + "bitbybit.polyline.maxFilletsHalfLine_description": "Calcula el radio máximo de redondeo de media línea posible para cada esquina de una polilínea dada. Para una polilínea cerrada, incluye las esquinas que conectan el último segmento con el primero. El cálculo utiliza la restricción de 'media línea', lo que significa que los puntos tangentes del redondeo deben estar dentro de la primera mitad de cada segmento conectado a la esquina.", + "bitbybit.polyline.safestFilletRadius": "radio de redondeo más seguro", + "safestFilletRadius": "radio de redondeo más seguro", + "bitbybit.polyline.safestFilletRadius_description": "Calcula el único radio máximo de redondeo más seguro que se puede aplicar uniformemente a todas las esquinas de una polilínea, basado en la restricción de 'media línea'. Esto se determina encontrando el mínimo de los radios máximos de redondeo posibles calculados para cada esquina individual.", + "flatTop": "parte superior plana", + "bitbybit.mesh.meshMeshIntersectionPoints": "puntos de intersección malla-malla", + "meshMeshIntersectionPoints": "puntos de intersección malla-malla", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Calcula los puntos de intersección de dos mallas.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "hexágonos en rejilla", + "hexagonsInGrid": "hexágonos en rejilla", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Crea alambres hexagonales de OpenCascade en una rejilla", + "scalePatternWidth": "escalar anchura del patrón", + "scalePatternHeight": "escalar altura del patrón", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "alambres de intersección malla-malla", + "meshMeshIntersectionWires": "alambres de intersección malla-malla", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Realiza una operación de intersección malla-malla entre dos formas; ambas formas pueden tener su propia precisión de mallado. Este algoritmo interseca las mallas y devuelve los alambres de la intersección, que son polilíneas o polígonos.", + "mesh based": "basado en malla", + "precision1": "precisión 1", + "precision2": "precisión 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "puntos de intersección malla-malla", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Realiza una operación de intersección malla-malla entre dos formas; ambas formas pueden tener su propia precisión de mallado. Este algoritmo interseca las mallas y devuelve los puntos de la intersección.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "intersección malla-malla a alambres", + "meshMeshIntersectionOfShapesWires": "intersección malla-malla a alambres", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Realiza una operación de intersección malla-malla entre la forma y múltiples otras formas; todas las formas pueden tener su propia precisión de mallado. Este algoritmo interseca las mallas y devuelve los alambres de la intersección, que son polilíneas o polígonos.", + "precisionShapes": "precisión formas", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "intersección malla-malla a puntos", + "meshMeshIntersectionOfShapesPoints": "intersección malla-malla a puntos", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Realiza una operación de intersección malla-malla entre la forma y múltiples otras formas; todas las formas pueden tener su propia precisión de mallado. Este algoritmo interseca las mallas y devuelve los puntos de la intersección.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "hexágonos en rejilla", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Crea hexágonos de OpenCascade en una rejilla (caras)", + "bitbybit.jscad.toPolygonPoints": "a puntos de polígono", + "toPolygonPoints": "a puntos de polígono", + "bitbybit.jscad.toPolygonPoints_description": "Convierte la forma jscad en una colección de puntos de polígono que representan la malla", + "conversions": "conversiones", + "bitbybit.manifold.toPolygonPoints": "a puntos de polígono", + "bitbybit.manifold.toPolygonPoints_description": "Convierte la forma manifold en una colección de puntos de polígono que representan la malla.", + "flatU": "plano U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "subdividir en agujeros hexagonales", + "subdivideToHexagonHoles": "subdividir en agujeros hexagonales", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Subdivide una cara en agujeros hexagonales", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "convertir líneas a curvas NURBS", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Convierte líneas en curvas NURBS. Devuelve un array de objetos verbnurbs Line.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "convertir línea a curva NURBS", + "convertLineToNurbsCurve": "convertir línea a curva NURBS", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Convierte una línea en una curva NURBS. Devuelve el objeto verbnurbs Line.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "convertir polilínea a curva NURBS", + "convertPolylineToNurbsCurve": "convertir polilínea a curva NURBS", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Convierte una polilínea en una curva NURBS. Devuelve el objeto verbnurbs NurbsCurve.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "convertir polilíneas a curvas NURBS", + "convertPolylinesToNurbsCurves": "convertir polilíneas a curvas NURBS", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Convierte polilíneas en curvas NURBS. Devuelve los objetos verbnurbs NurbsCurve.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/fr.json b/languages/fr.json index b9e8f5cc..8a633d5c 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Crée un maillage à partir de la forme", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "à partir de points de polygone", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Créer une variété (Manifold) à partir d'un ensemble de points de polygone décrivant des triangles.", - "traingle": "triangle" -} + "traingle": "triangle", + "bitbybit.point.stretchPointsDirFromCenter": "étirer les points depuis le centre dans une direction", + "stretchPointsDirFromCenter": "étirer les points depuis le centre dans une direction", + "bitbybit.point.stretchPointsDirFromCenter_description": "Étire plusieurs points en fournissant un point central, une direction et un facteur d'échelle uniforme", + "bitbybit.point.hexGridScaledToFit": "grille hexagonale mise à l'échelle pour s'adapter", + "hexGridScaledToFit": "grille hexagonale mise à l'échelle pour s'adapter", + "bitbybit.point.hexGridScaledToFit_description": "Crée une grille hexagonale à sommet pointu, mettant à l'échelle les hexagones pour s'adapter exactement aux dimensions spécifiées. Renvoie à la fois les points centraux et les sommets de chaque hexagone (potentiellement mis à l'échelle). Les hexagones sont ordonnés d'abord par colonne, puis par ligne.", + "nrHexagonsU": "nb hexagones U", + "nrHexagonsV": "nb hexagones V", + "extendTop": "étendre haut", + "extendBottom": "étendre bas", + "extendLeft": "étendre gauche", + "extendRight": "étendre droite", + "centerGrid": "centrer la grille", + "bitbybit.point.sortPoints": "trier les points", + "sortPoints": "trier les points", + "bitbybit.point.sortPoints_description": "Trie les points lexicographiquement (X, puis Y, puis Z)", + "bitbybit.line.lineLineIntersection": "intersection ligne-ligne", + "lineLineIntersection": "intersection ligne-ligne", + "bitbybit.line.lineLineIntersection_description": "Si deux lignes se croisent, renvoie le point d'intersection", + "line1": "ligne 1", + "line2": "ligne 2", + "checkSegmentsOnly": "vérifier les segments uniquement", + "bitbybit.polyline.polylineToLines": "polyligne vers lignes", + "polylineToLines": "polyligne vers lignes", + "bitbybit.polyline.polylineToLines_description": "Crée les lignes à partir de la polyligne", + "bitbybit.polyline.polylineToSegments": "polyligne vers segments", + "polylineToSegments": "polyligne vers segments", + "bitbybit.polyline.polylineToSegments_description": "Crée les segments à partir de la polyligne", + "bitbybit.polyline.polylineSelfIntersection": "auto-intersection de polyligne", + "polylineSelfIntersection": "auto-intersection de polyligne", + "bitbybit.polyline.polylineSelfIntersection_description": "Trouve les points d'auto-intersection de la polyligne", + "bitbybit.polyline.twoPolylineIntersection": "intersection de deux polylignes", + "twoPolylineIntersection": "intersection de deux polylignes", + "bitbybit.polyline.twoPolylineIntersection_description": "Trouve les points d'intersection entre deux polylignes.", + "polyline1": "polyligne 1", + "polyline2": "polyligne 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "subdiviser en fils hexagonaux", + "subdivideToHexagonWires": "subdiviser en fils hexagonaux", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Subdivise une face en fils hexagonaux", + "extendUUp": "étendre U haut", + "extendUBottom": "étendre U bas", + "extendVUp": "étendre V haut", + "extendVBottom": "étendre V bas", + "nrHexagonsInHeight": "nb hexagones hauteur", + "nrHexagonsInWidth": "nb hexagones largeur", + "bitbybit.vector.length": "longueur du vecteur", + "bitbybit.vector.length_description": "Calcule la longueur du vecteur", + "bitbybit.point.maxFilletRadius": "rayon de congé maximal", + "maxFilletRadius": "rayon de congé maximal", + "bitbybit.point.maxFilletRadius_description": "Calcule le rayon de congé maximal possible à un coin formé par deux segments de ligne partageant une extrémité (C), de sorte que l'arc de congé soit tangent aux deux segments et se situe entièrement à l'intérieur de ceux-ci.", + "bitbybit.point.maxFilletRadiusHalfLine": "rayon de congé maximal demi-ligne", + "maxFilletRadiusHalfLine": "rayon de congé maximal demi-ligne", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Calcule le rayon de congé maximal possible à un coin C, de sorte que l'arc de congé soit tangent aux deux segments (P1-C, P2-C) et que les points tangents se situent dans la première moitié de chaque segment (mesurée à partir de C).", + "bitbybit.point.maxFilletsHalfLine": "congés maximaux demi-ligne", + "maxFilletsHalfLine": "congés maximaux demi-ligne", + "bitbybit.point.maxFilletsHalfLine_description": "Calcule le rayon de congé maximal possible à chaque coin d'une polyligne formée par une série de points. Le rayon de congé est calculé pour chaque coin interne et éventuellement pour les coins de fermeture si la polyligne est fermée.", + "checkLastWithFirst": "vérifier dernier avec premier", + "bitbybit.point.safestPointsMaxFilletHalfLine": "rayon de congé maximal le plus sûr (points, demi-ligne)", + "safestPointsMaxFilletHalfLine": "rayon de congé maximal le plus sûr (points, demi-ligne)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Calcule le rayon de congé maximal le plus sûr unique qui peut être appliqué uniformément à tous les coins d'une collection de points, basé sur la contrainte 'demi-ligne'. Ceci est déterminé en trouvant le minimum des rayons de congé maximaux possibles calculés pour chaque coin individuel.", + "bitbybit.polyline.maxFilletsHalfLine": "congés maximaux demi-ligne", + "bitbybit.polyline.maxFilletsHalfLine_description": "Calcule le rayon de congé maximal possible 'demi-ligne' pour chaque coin d'une polyligne donnée. Pour une polyligne fermée, il inclut les coins reliant le dernier segment au premier. Le calcul utilise la contrainte 'demi-ligne', ce qui signifie que les points tangents du congé doivent se situer dans la première moitié de chaque segment connecté au coin.", + "bitbybit.polyline.safestFilletRadius": "rayon de congé le plus sûr", + "safestFilletRadius": "rayon de congé le plus sûr", + "bitbybit.polyline.safestFilletRadius_description": "Calcule le rayon de congé maximal le plus sûr unique qui peut être appliqué uniformément à tous les coins d'une polyligne, basé sur la contrainte 'demi-ligne'. Ceci est déterminé en trouvant le minimum des rayons de congé maximaux possibles calculés pour chaque coin individuel.", + "flatTop": "sommet plat", + "bitbybit.mesh.meshMeshIntersectionPoints": "points d'intersection maillage-maillage", + "meshMeshIntersectionPoints": "points d'intersection maillage-maillage", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Calcule les points d'intersection de deux maillages.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "hexagones dans la grille", + "hexagonsInGrid": "hexagones dans la grille", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Crée des fils hexagonaux OpenCascade dans une grille", + "scalePatternWidth": "échelle largeur motif", + "scalePatternHeight": "échelle hauteur motif", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "fils d'intersection maillage-maillage", + "meshMeshIntersectionWires": "fils d'intersection maillage-maillage", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Effectue une opération d'intersection maillage-maillage entre deux formes - les deux formes peuvent avoir leur propre précision de maillage. Cet algorithme intersecte les maillages et renvoie les fils de l'intersection, qui sont des polylignes ou des polygones.", + "mesh based": "basé sur maillage", + "precision1": "précision 1", + "precision2": "précision 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "points d'intersection maillage-maillage", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Effectue une opération d'intersection maillage-maillage entre deux formes - les deux formes peuvent avoir leur propre précision de maillage. Cet algorithme intersecte les maillages et renvoie les points de l'intersection.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "intersection maillage-maillage vers fils", + "meshMeshIntersectionOfShapesWires": "intersection maillage-maillage vers fils", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Effectue une opération d'intersection maillage-maillage entre la forme et plusieurs autres formes - toutes les formes peuvent avoir leur propre précision de maillage. Cet algorithme intersecte les maillages et renvoie les fils de l'intersection, qui sont des polylignes ou des polygones.", + "precisionShapes": "précision formes", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "intersection maillage-maillage vers points", + "meshMeshIntersectionOfShapesPoints": "intersection maillage-maillage vers points", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Effectue une opération d'intersection maillage-maillage entre la forme et plusieurs autres formes - toutes les formes peuvent avoir leur propre précision de maillage. Cet algorithme intersecte les maillages et renvoie les points de l'intersection.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "hexagones dans la grille", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Crée des hexagones OpenCascade dans une grille (faces)", + "bitbybit.jscad.toPolygonPoints": "vers points de polygone", + "toPolygonPoints": "vers points de polygone", + "bitbybit.jscad.toPolygonPoints_description": "Transforme la forme jscad en une collection de points de polygone représentant le maillage", + "conversions": "conversions", + "bitbybit.manifold.toPolygonPoints": "vers points de polygone", + "bitbybit.manifold.toPolygonPoints_description": "Transforme la forme manifold en une collection de points de polygone représentant le maillage.", + "flatU": "plat U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "subdiviser en trous hexagonaux", + "subdivideToHexagonHoles": "subdiviser en trous hexagonaux", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Subdivise une face en trous hexagonaux", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "convertir les lignes en courbes NURBS", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Convertit les lignes en courbes NURBS. Renvoie un tableau d'objets verbnurbs Line.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "convertir une ligne en courbe NURBS", + "convertLineToNurbsCurve": "convertir une ligne en courbe NURBS", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Convertit une ligne en courbe NURBS. Renvoie l'objet verbnurbs Line.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "convertir une polyligne en courbe NURBS", + "convertPolylineToNurbsCurve": "convertir une polyligne en courbe NURBS", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Convertit une polyligne en courbe NURBS. Renvoie l'objet verbnurbs NurbsCurve.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "convertir les polylignes en courbes NURBS", + "convertPolylinesToNurbsCurves": "convertir les polylignes en courbes NURBS", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Convertit les polylignes en courbes NURBS. Renvoie les objets verbnurbs NurbsCurve.", + "Base.Polyline3[]": "Base.Polyline3[]" +} \ No newline at end of file diff --git a/languages/hi.json b/languages/hi.json index 947937df..5e2ed42a 100644 --- a/languages/hi.json +++ b/languages/hi.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "आकृति से मेश बनाता है", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "बहुभुज बिंदुओं से", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "त्रिभुजों का वर्णन करने वाले बहुभुज बिंदुओं के एक सेट से एक मैनिफोल्ड (Manifold) बनाएं।", - "traingle": "त्रिभुज" + "traingle": "त्रिभुज", + "bitbybit.point.stretchPointsDirFromCenter": "केंद्र से दिशा में बिंदुओं को खींचे", + "stretchPointsDirFromCenter": "केंद्र से दिशा में बिंदुओं को खींचे", + "bitbybit.point.stretchPointsDirFromCenter_description": "केंद्र बिंदु, दिशा और समान पैमाने कारक प्रदान करके कई बिंदुओं को खींचे", + "bitbybit.point.hexGridScaledToFit": "फिट करने के लिए स्केल किया गया हेक्स ग्रिड", + "hexGridScaledToFit": "फिट करने के लिए स्केल किया गया हेक्स ग्रिड", + "bitbybit.point.hexGridScaledToFit_description": "एक नुकीला शीर्ष षट्कोण ग्रिड बनाता है, षट्कोणों को निर्दिष्ट आयामों में सटीक रूप से फिट करने के लिए मापता है। प्रत्येक (संभावित रूप से स्केल किए गए) षट्कोण के केंद्र बिंदु और कोने दोनों लौटाता है। षट्कोण पहले कॉलम-वार, फिर पंक्ति-वार क्रमबद्ध होते हैं।", + "nrHexagonsU": "षट्कोणों की संख्या U", + "nrHexagonsV": "षट्कोणों की संख्या V", + "extendTop": "ऊपर बढ़ाएँ", + "extendBottom": "नीचे बढ़ाएँ", + "extendLeft": "बाएँ बढ़ाएँ", + "extendRight": "दाएँ बढ़ाएँ", + "centerGrid": "ग्रिड को केंद्र में रखें", + "bitbybit.point.sortPoints": "बिंदुओं को क्रमित करें", + "sortPoints": "बिंदुओं को क्रमित करें", + "bitbybit.point.sortPoints_description": "बिंदुओं को शब्दकोषीय रूप से क्रमित करता है (X, फिर Y, फिर Z)", + "bitbybit.line.lineLineIntersection": "रेखा-रेखा प्रतिच्छेदन", + "lineLineIntersection": "रेखा-रेखा प्रतिच्छेदन", + "bitbybit.line.lineLineIntersection_description": "यदि दो रेखाएँ प्रतिच्छेद करती हैं, तो प्रतिच्छेदन बिंदु लौटाएँ", + "line1": "रेखा 1", + "line2": "रेखा 2", + "checkSegmentsOnly": "केवल खंडों की जाँच करें", + "bitbybit.polyline.polylineToLines": "पॉलीलाइन से रेखाएँ", + "polylineToLines": "पॉलीलाइन से रेखाएँ", + "bitbybit.polyline.polylineToLines_description": "पॉलीलाइन से रेखाएँ बनाएँ", + "bitbybit.polyline.polylineToSegments": "पॉलीलाइन से खंड", + "polylineToSegments": "पॉलीलाइन से खंड", + "bitbybit.polyline.polylineToSegments_description": "पॉलीलाइन से खंड बनाएँ", + "bitbybit.polyline.polylineSelfIntersection": "पॉलीलाइन स्व-प्रतिच्छेदन", + "polylineSelfIntersection": "पॉलीलाइन स्व-प्रतिच्छेदन", + "bitbybit.polyline.polylineSelfIntersection_description": "पॉलीलाइन के स्व-प्रतिच्छेदन के बिंदु ढूँढता है", + "bitbybit.polyline.twoPolylineIntersection": "दो पॉलीलाइन प्रतिच्छेदन", + "twoPolylineIntersection": "दो पॉलीलाइन प्रतिच्छेदन", + "bitbybit.polyline.twoPolylineIntersection_description": "दो पॉलीलाइनों के बीच प्रतिच्छेदन बिंदु ढूँढता है।", + "polyline1": "पॉलीलाइन 1", + "polyline2": "पॉलीलाइन 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "षट्कोण तारों में उप-विभाजित करें", + "subdivideToHexagonWires": "षट्कोण तारों में उप-विभाजित करें", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "एक फलक को षट्कोण तारों में उप-विभाजित करता है", + "extendUUp": "U ऊपर बढ़ाएँ", + "extendUBottom": "U नीचे बढ़ाएँ", + "extendVUp": "V ऊपर बढ़ाएँ", + "extendVBottom": "V नीचे बढ़ाएँ", + "nrHexagonsInHeight": "ऊंचाई में षट्कोणों की संख्या", + "nrHexagonsInWidth": "चौड़ाई में षट्कोणों की संख्या", + "bitbybit.vector.length": "सदिश लंबाई", + "bitbybit.vector.length_description": "सदिश की लंबाई की गणना करता है", + "bitbybit.point.maxFilletRadius": "अधिकतम फिलेट त्रिज्या", + "maxFilletRadius": "अधिकतम फिलेट त्रिज्या", + "bitbybit.point.maxFilletRadius_description": "दो रेखा खंडों द्वारा साझा किए गए अंतिम बिंदु (C) पर बने कोने पर अधिकतम संभव फिलेट त्रिज्या की गणना करता है, ताकि फिलेट चाप दोनों खंडों के स्पर्शरेखीय हो और पूरी तरह से उनके भीतर स्थित हो।", + "bitbybit.point.maxFilletRadiusHalfLine": "अधिकतम फिलेट त्रिज्या आधी रेखा", + "maxFilletRadiusHalfLine": "अधिकतम फिलेट त्रिज्या आधी रेखा", + "bitbybit.point.maxFilletRadiusHalfLine_description": "कोने C पर अधिकतम संभव फिलेट त्रिज्या की गणना करता है, ताकि फिलेट चाप दोनों खंडों (P1-C, P2-C) के स्पर्शरेखीय हो और स्पर्श बिंदु प्रत्येक खंड के पहले आधे हिस्से में (C से मापा गया) स्थित हों।", + "bitbybit.point.maxFilletsHalfLine": "अधिकतम फिलेट्स आधी रेखा", + "maxFilletsHalfLine": "अधिकतम फिलेट्स आधी रेखा", + "bitbybit.point.maxFilletsHalfLine_description": "बिंदुओं की एक श्रृंखला द्वारा गठित पॉलीलाइन के प्रत्येक कोने पर अधिकतम संभव फिलेट त्रिज्या की गणना करता है। फिलेट त्रिज्या की गणना प्रत्येक आंतरिक कोने के लिए और वैकल्पिक रूप से समापन कोनों के लिए की जाती है यदि पॉलीलाइन बंद है।", + "checkLastWithFirst": "अंतिम की पहले से जाँच करें", + "bitbybit.point.safestPointsMaxFilletHalfLine": "सबसे सुरक्षित बिंदु अधिकतम फिलेट आधी रेखा", + "safestPointsMaxFilletHalfLine": "सबसे सुरक्षित बिंदु अधिकतम फिलेट आधी रेखा", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "बिंदुओं के संग्रह के सभी कोनों पर समान रूप से लागू किए जा सकने वाले एकल सबसे सुरक्षित अधिकतम फिलेट त्रिज्या की गणना करता है, जो 'आधी-रेखा' बाधा पर आधारित है। यह प्रत्येक व्यक्तिगत कोने के लिए गणना की गई अधिकतम संभव फिलेट त्रिज्याओं में से न्यूनतम ज्ञात करके निर्धारित किया जाता है।", + "bitbybit.polyline.maxFilletsHalfLine": "अधिकतम फिलेट्स आधी रेखा", + "bitbybit.polyline.maxFilletsHalfLine_description": "किसी दी गई पॉलीलाइन के प्रत्येक कोने के लिए अधिकतम संभव हाफ-लाइन फिलेट त्रिज्या की गणना करता है। एक बंद पॉलीलाइन के लिए, इसमें अंतिम खंड को पहले से जोड़ने वाले कोने शामिल हैं। गणना 'हाफ-लाइन' बाधा का उपयोग करती है, जिसका अर्थ है कि फिलेट के स्पर्श बिंदु कोने से जुड़े प्रत्येक खंड के पहले आधे हिस्से में स्थित होने चाहिए।", + "bitbybit.polyline.safestFilletRadius": "सबसे सुरक्षित फिलेट त्रिज्या", + "safestFilletRadius": "सबसे सुरक्षित फिलेट त्रिज्या", + "bitbybit.polyline.safestFilletRadius_description": "पॉलीलाइन के सभी कोनों पर समान रूप से लागू किए जा सकने वाले एकल सबसे सुरक्षित अधिकतम फिलेट त्रिज्या की गणना करता है, जो 'आधी-रेखा' बाधा पर आधारित है। यह प्रत्येक व्यक्तिगत कोने के लिए गणना की गई अधिकतम संभव फिलेट त्रिज्याओं में से न्यूनतम ज्ञात करके निर्धारित किया जाता है।", + "flatTop": "समतल शीर्ष", + "bitbybit.mesh.meshMeshIntersectionPoints": "मेश-मेश प्रतिच्छेदन बिंदु", + "meshMeshIntersectionPoints": "मेश-मेश प्रतिच्छेदन बिंदु", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "दो मेश के प्रतिच्छेदन बिंदुओं की गणना करता है।", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "ग्रिड में षट्कोण", + "hexagonsInGrid": "ग्रिड में षट्कोण", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "ग्रिड में OpenCascade षट्कोण तार बनाता है", + "scalePatternWidth": "पैटर्न चौड़ाई स्केल करें", + "scalePatternHeight": "पैटर्न ऊंचाई स्केल करें", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "मेश-मेश प्रतिच्छेदन तार", + "meshMeshIntersectionWires": "मेश-मेश प्रतिच्छेदन तार", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "दो आकृतियों के बीच मेश-मेश प्रतिच्छेदन ऑपरेशन करता है - दोनों आकृतियों की अपनी मेशिंग परिशुद्धता हो सकती है। यह एल्गोरिथ्म मेश को प्रतिच्छेद करता है और प्रतिच्छेदन के तार लौटाता है, जो पॉलीलाइन या बहुभुज होते हैं।", + "mesh based": "मेश आधारित", + "precision1": "परिशुद्धता 1", + "precision2": "परिशुद्धता 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "मेश-मेश प्रतिच्छेदन बिंदु", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "दो आकृतियों के बीच मेश-मेश प्रतिच्छेदन ऑपरेशन करता है - दोनों आकृतियों की अपनी मेशिंग परिशुद्धता हो सकती है। यह एल्गोरिथ्म मेश को प्रतिच्छेद करता है और प्रतिच्छेदन के बिंदु लौटाता है।", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "मेश-मेश प्रतिच्छेदन से तार", + "meshMeshIntersectionOfShapesWires": "मेश-मेश प्रतिच्छेदन से तार", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "आकृति और कई अन्य आकृतियों के बीच मेश-मेश प्रतिच्छेदन ऑपरेशन करता है - सभी आकृतियों की अपनी मेशिंग परिशुद्धता हो सकती है। यह एल्गोरिथ्म मेश को प्रतिच्छेद करता है और प्रतिच्छेदन के तार लौटाता है, जो पॉलीलाइन या बहुभुज होते हैं।", + "precisionShapes": "आकृतियों की परिशुद्धता", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "मेश-मेश प्रतिच्छेदन से बिंदु", + "meshMeshIntersectionOfShapesPoints": "मेश-मेश प्रतिच्छेदन से बिंदु", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "आकृति और कई अन्य आकृतियों के बीच मेश-मेश प्रतिच्छेदन ऑपरेशन करता है - सभी आकृतियों की अपनी मेशिंग परिशुद्धता हो सकती है। यह एल्गोरिथ्म मेश को प्रतिच्छेद करता है और प्रतिच्छेदन के बिंदु लौटाता है।", + "bitbybit.occt.shapes.face.hexagonsInGrid": "ग्रिड में षट्कोण", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "ग्रिड में OpenCascade षट्कोण बनाता है (फलक)", + "bitbybit.jscad.toPolygonPoints": "बहुभुज बिंदुओं में", + "toPolygonPoints": "बहुभुज बिंदुओं में", + "bitbybit.jscad.toPolygonPoints_description": "jscad आकृति को मेश का प्रतिनिधित्व करने वाले बहुभुज बिंदुओं के संग्रह में बदलता है", + "conversions": "रूपांतरण", + "bitbybit.manifold.toPolygonPoints": "बहुभुज बिंदुओं में", + "bitbybit.manifold.toPolygonPoints_description": "मैनिफोल्ड आकृति को मेश का प्रतिनिधित्व करने वाले बहुभुज बिंदुओं के संग्रह में बदलता है।", + "flatU": "समतल U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "षट्कोण छेदों में उप-विभाजित करें", + "subdivideToHexagonHoles": "षट्कोण छेदों में उप-विभाजित करें", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "एक फलक को षट्कोण छेदों में उप-विभाजित करता है", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "रेखाओं को NURBS वक्रों में बदलें", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "रेखाओं को NURBS वक्रों में परिवर्तित करता है। verbnurbs Line ऑब्जेक्ट्स का ऐरे लौटाता है।", + "bitbybit.verb.curve.convertLineToNurbsCurve": "रेखा को NURBS वक्र में बदलें", + "convertLineToNurbsCurve": "रेखा को NURBS वक्र में बदलें", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "रेखा को NURBS वक्र में परिवर्तित करता है। verbnurbs Line ऑब्जेक्ट लौटाता है।", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "पॉलीलाइन को NURBS वक्र में बदलें", + "convertPolylineToNurbsCurve": "पॉलीलाइन को NURBS वक्र में बदलें", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "एक पॉलीलाइन को NURBS वक्र में परिवर्तित करता है। verbnurbs NurbsCurve ऑब्जेक्ट लौटाता है।", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "पॉलीलाइनों को NURBS वक्रों में बदलें", + "convertPolylinesToNurbsCurves": "पॉलीलाइनों को NURBS वक्रों में बदलें", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "पॉलीलाइनों को NURBS वक्रों में परिवर्तित करता है। verbnurbs NurbsCurve ऑब्जेक्ट्स लौटाता है।", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/id.json b/languages/id.json index b980c16c..ac00d6bd 100644 --- a/languages/id.json +++ b/languages/id.json @@ -3204,7 +3204,7 @@ "skinCellBottomHeight": "tinggi bawah sel kulit", "skinCellTopHeight": "tinggi atas sel kulit", "bottomThickness": "ketebalan bawah", -"bitbybit.things.threeDPrinting.cups.dragonCup.getCompoundShape": "dapatkan bentuk compound", + "bitbybit.things.threeDPrinting.cups.dragonCup.getCompoundShape": "dapatkan bentuk compound", "bitbybit.things.threeDPrinting.cups.dragonCup.getCompoundShape_description": "mendapatkan bentuk compound cangkir naga", "DragonCupData": "data cangkir naga t", "bitbybit.things.threeDPrinting.desktop.phoneNest.create": "sarang telepon", @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Membuat mesh dari bentuk", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "dari titik poligon", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Buat Manifold dari sekumpulan titik poligon yang mendeskripsikan segitiga.", - "traingle": "segitiga" + "traingle": "segitiga", + "bitbybit.point.stretchPointsDirFromCenter": "rentangkan titik arah dari pusat", + "stretchPointsDirFromCenter": "rentangkan titik arah dari pusat", + "bitbybit.point.stretchPointsDirFromCenter_description": "Rentangkan beberapa titik dengan memberikan titik pusat, arah, dan faktor skala seragam", + "bitbybit.point.hexGridScaledToFit": "kisi heksagonal diskalakan agar pas", + "hexGridScaledToFit": "kisi heksagonal diskalakan agar pas", + "bitbybit.point.hexGridScaledToFit_description": "Membuat kisi heksagon puncak runcing, menskalakan heksagon agar pas dengan dimensi yang ditentukan secara tepat. Mengembalikan titik pusat dan simpul dari setiap heksagon (yang berpotensi diskalakan). Heksagon diurutkan kolom-pertama, lalu baris-pertama.", + "nrHexagonsU": "jumlah heksagon U", + "nrHexagonsV": "jumlah heksagon V", + "extendTop": "perluas atas", + "extendBottom": "perluas bawah", + "extendLeft": "perluas kiri", + "extendRight": "perluas kanan", + "centerGrid": "pusatkan kisi", + "bitbybit.point.sortPoints": "urutkan titik", + "sortPoints": "urutkan titik", + "bitbybit.point.sortPoints_description": "Mengurutkan titik secara leksikografis (X, lalu Y, lalu Z)", + "bitbybit.line.lineLineIntersection": "perpotongan garis-garis", + "lineLineIntersection": "perpotongan garis-garis", + "bitbybit.line.lineLineIntersection_description": "Jika dua garis berpotongan, kembalikan titik potong", + "line1": "garis 1", + "line2": "garis 2", + "checkSegmentsOnly": "periksa segmen saja", + "bitbybit.polyline.polylineToLines": "poliline ke garis", + "polylineToLines": "poliline ke garis", + "bitbybit.polyline.polylineToLines_description": "Buat garis dari poliline", + "bitbybit.polyline.polylineToSegments": "poliline ke segmen", + "polylineToSegments": "poliline ke segmen", + "bitbybit.polyline.polylineToSegments_description": "Buat segmen dari poliline", + "bitbybit.polyline.polylineSelfIntersection": "perpotongan diri poliline", + "polylineSelfIntersection": "perpotongan diri poliline", + "bitbybit.polyline.polylineSelfIntersection_description": "Menemukan titik-titik perpotongan diri dari poliline", + "bitbybit.polyline.twoPolylineIntersection": "perpotongan dua poliline", + "twoPolylineIntersection": "perpotongan dua poliline", + "bitbybit.polyline.twoPolylineIntersection_description": "Menemukan titik potong antara dua poliline.", + "polyline1": "poliline 1", + "polyline2": "poliline 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "bagi menjadi kawat heksagon", + "subdivideToHexagonWires": "bagi menjadi kawat heksagon", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Membagi permukaan menjadi kawat heksagon", + "extendUUp": "perluas U atas", + "extendUBottom": "perluas U bawah", + "extendVUp": "perluas V atas", + "extendVBottom": "perluas V bawah", + "nrHexagonsInHeight": "jumlah heksagon tinggi", + "nrHexagonsInWidth": "jumlah heksagon lebar", + "bitbybit.vector.length": "panjang vektor", + "bitbybit.vector.length_description": "Menghitung panjang vektor", + "bitbybit.point.maxFilletRadius": "radius fillet maks", + "maxFilletRadius": "radius fillet maks", + "bitbybit.point.maxFilletRadius_description": "Menghitung radius fillet maksimum yang mungkin pada sudut yang dibentuk oleh dua segmen garis yang berbagi titik akhir (C), sedemikian rupa sehingga busur fillet bersinggungan dengan kedua segmen dan terletak sepenuhnya di dalamnya.", + "bitbybit.point.maxFilletRadiusHalfLine": "radius fillet maks setengah garis", + "maxFilletRadiusHalfLine": "radius fillet maks setengah garis", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Menghitung radius fillet maksimum yang mungkin pada sudut C, sedemikian rupa sehingga busur fillet bersinggungan dengan kedua segmen (P1-C, P2-C) dan titik singgung terletak di dalam paruh pertama setiap segmen (diukur dari C).", + "bitbybit.point.maxFilletsHalfLine": "fillet maks setengah garis", + "maxFilletsHalfLine": "fillet maks setengah garis", + "bitbybit.point.maxFilletsHalfLine_description": "Menghitung radius fillet maksimum yang mungkin pada setiap sudut poliline yang dibentuk oleh serangkaian titik. Radius fillet dihitung untuk setiap sudut internal dan secara opsional untuk sudut penutup jika poliline tertutup.", + "checkLastWithFirst": "periksa terakhir dengan pertama", + "bitbybit.point.safestPointsMaxFilletHalfLine": "radius fillet maks teraman titik setengah garis", + "safestPointsMaxFilletHalfLine": "radius fillet maks teraman titik setengah garis", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Menghitung radius fillet maksimum teraman tunggal yang dapat diterapkan secara seragam ke semua sudut kumpulan titik, berdasarkan batasan 'setengah garis'. Ini ditentukan dengan menemukan minimum dari radius fillet maksimum yang mungkin dihitung untuk setiap sudut individu.", + "bitbybit.polyline.maxFilletsHalfLine": "fillet maks setengah garis", + "bitbybit.polyline.maxFilletsHalfLine_description": "Menghitung radius fillet setengah garis maksimum yang mungkin untuk setiap sudut dari poliline yang diberikan. Untuk poliline tertutup, ini mencakup sudut yang menghubungkan segmen terakhir kembali ke yang pertama. Perhitungan menggunakan batasan 'setengah garis', yang berarti titik singgung fillet harus terletak di dalam paruh pertama setiap segmen yang terhubung ke sudut.", + "bitbybit.polyline.safestFilletRadius": "radius fillet teraman", + "safestFilletRadius": "radius fillet teraman", + "bitbybit.polyline.safestFilletRadius_description": "Menghitung radius fillet maksimum teraman tunggal yang dapat diterapkan secara seragam ke semua sudut poliline, berdasarkan batasan 'setengah garis'. Ini ditentukan dengan menemukan minimum dari radius fillet maksimum yang mungkin dihitung untuk setiap sudut individu.", + "flatTop": "puncak datar", + "bitbybit.mesh.meshMeshIntersectionPoints": "titik potong mesh-mesh", + "meshMeshIntersectionPoints": "titik potong mesh-mesh", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Menghitung titik potong dua mesh.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "heksagon dalam kisi", + "hexagonsInGrid": "heksagon dalam kisi", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Membuat kawat heksagon OpenCascade dalam kisi", + "scalePatternWidth": "skala lebar pola", + "scalePatternHeight": "skala tinggi pola", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "kawat potong mesh-mesh", + "meshMeshIntersectionWires": "kawat potong mesh-mesh", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Melakukan operasi perpotongan mesh-mesh antara dua bentuk - kedua bentuk dapat memiliki presisi meshing sendiri. Algoritma ini memotong mesh dan mengembalikan kawat perpotongan, yang merupakan poliline atau poligon.", + "mesh based": "berbasis mesh", + "precision1": "presisi 1", + "precision2": "presisi 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "titik potong mesh-mesh", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Melakukan operasi perpotongan mesh-mesh antara dua bentuk - kedua bentuk dapat memiliki presisi meshing sendiri. Algoritma ini memotong mesh dan mengembalikan titik-titik perpotongan.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "perpotongan mesh-mesh ke kawat", + "meshMeshIntersectionOfShapesWires": "perpotongan mesh-mesh ke kawat", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Melakukan operasi perpotongan mesh-mesh antara bentuk dan beberapa bentuk lain - semua bentuk dapat memiliki presisi meshing sendiri. Algoritma ini memotong mesh dan mengembalikan kawat perpotongan, yang merupakan poliline atau poligon.", + "precisionShapes": "presisi bentuk", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "perpotongan mesh-mesh ke titik", + "meshMeshIntersectionOfShapesPoints": "perpotongan mesh-mesh ke titik", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Melakukan operasi perpotongan mesh-mesh antara bentuk dan beberapa bentuk lain - semua bentuk dapat memiliki presisi meshing sendiri. Algoritma ini memotong mesh dan mengembalikan titik-titik perpotongan.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "heksagon dalam kisi", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Membuat heksagon OpenCascade dalam kisi (permukaan)", + "bitbybit.jscad.toPolygonPoints": "ke titik poligon", + "toPolygonPoints": "ke titik poligon", + "bitbybit.jscad.toPolygonPoints_description": "Mengubah bentuk jscad menjadi kumpulan titik poligon yang mewakili mesh", + "conversions": "konversi", + "bitbybit.manifold.toPolygonPoints": "ke titik poligon", + "bitbybit.manifold.toPolygonPoints_description": "Mengubah bentuk manifold menjadi kumpulan titik poligon yang mewakili mesh.", + "flatU": "datar U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "bagi menjadi lubang heksagon", + "subdivideToHexagonHoles": "bagi menjadi lubang heksagon", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Membagi permukaan menjadi lubang heksagon", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "konversi garis ke kurva NURBS", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Mengonversi garis menjadi kurva NURBS. Mengembalikan array objek verbnurbs Line.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "konversi garis ke kurva NURBS", + "convertLineToNurbsCurve": "konversi garis ke kurva NURBS", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Mengonversi garis menjadi kurva NURBS. Mengembalikan objek verbnurbs Line.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "konversi poliline ke kurva NURBS", + "convertPolylineToNurbsCurve": "konversi poliline ke kurva NURBS", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Mengonversi poliline menjadi kurva NURBS. Mengembalikan objek verbnurbs NurbsCurve.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "konversi poliline ke kurva NURBS", + "convertPolylinesToNurbsCurves": "konversi poliline ke kurva NURBS", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Mengonversi poliline menjadi kurva NURBS. Mengembalikan objek verbnurbs NurbsCurve.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/lt.json b/languages/lt.json index ac03bebd..5af3f9f1 100644 --- a/languages/lt.json +++ b/languages/lt.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Sukuria tinklą iš formos", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "iš daugiakampio taškų", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Sukurti kolektorių iš daugiakampio taškų aibės, aprašančios trikampius.", - "traingle": "trikampis" + "traingle": "trikampis", + "bitbybit.point.stretchPointsDirFromCenter": "taškų tempimas kryptimi nuo centro", + "stretchPointsDirFromCenter": "taškų tempimas kryptimi nuo centro", + "bitbybit.point.stretchPointsDirFromCenter_description": "Ištempia kelis taškus, pateikiant centro tašką, kryptį ir vienodą mastelio koeficientą", + "bitbybit.point.hexGridScaledToFit": "pagal matmenis masteliuotas šešiakampių tinklelis", + "hexGridScaledToFit": "pagal matmenis masteliuotas šešiakampių tinklelis", + "bitbybit.point.hexGridScaledToFit_description": "Sukuria šešiakampių tinklelį smailiomis viršūnėmis į viršų, keičiant šešiakampių mastelį, kad tiksliai atitiktų nurodytus matmenis. Grąžina tiek centro taškus, tiek kiekvieno (galimai masteliuoto) šešiakampio viršūnes. Šešiakampiai išdėstyti pirmiausia stulpeliais, paskui eilutėmis.", + "nrHexagonsU": "šešiakampių sk. U", + "nrHexagonsV": "šešiakampių sk. V", + "extendTop": "pratęsti viršuje", + "extendBottom": "pratęsti apačioje", + "extendLeft": "pratęsti kairėje", + "extendRight": "pratęsti dešinėje", + "centerGrid": "centruoti tinklelį", + "bitbybit.point.sortPoints": "rūšiuoti taškus", + "sortPoints": "rūšiuoti taškus", + "bitbybit.point.sortPoints_description": "Rūšiuoja taškus leksikografiškai (X, tada Y, tada Z)", + "bitbybit.line.lineLineIntersection": "linijos ir linijos sankirta", + "lineLineIntersection": "linijos ir linijos sankirta", + "bitbybit.line.lineLineIntersection_description": "Jei dvi linijos kertasi, grąžina sankirtos tašką", + "line1": "linija 1", + "line2": "linija 2", + "checkSegmentsOnly": "tikrinti tik atkarpas", + "bitbybit.polyline.polylineToLines": "polilinija į linijas", + "polylineToLines": "polilinija į linijas", + "bitbybit.polyline.polylineToLines_description": "Sukuria linijas iš polilinijos", + "bitbybit.polyline.polylineToSegments": "polilinija į atkarpas", + "polylineToSegments": "polilinija į atkarpas", + "bitbybit.polyline.polylineToSegments_description": "Sukuria atkarpas iš polilinijos", + "bitbybit.polyline.polylineSelfIntersection": "polilinijos savęs sankirta", + "polylineSelfIntersection": "polilinijos savęs sankirta", + "bitbybit.polyline.polylineSelfIntersection_description": "Randa polilinijos savęs sankirtos taškus", + "bitbybit.polyline.twoPolylineIntersection": "dviejų polilinijų sankirta", + "twoPolylineIntersection": "dviejų polilinijų sankirta", + "bitbybit.polyline.twoPolylineIntersection_description": "Randa sankirtos taškus tarp dviejų polilinijų.", + "polyline1": "polilinija 1", + "polyline2": "polilinija 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "suskirstyti į šešiakampius kontūrus", + "subdivideToHexagonWires": "suskirstyti į šešiakampius kontūrus", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Suskirsto paviršių į šešiakampius kontūrus", + "extendUUp": "pratęsti U aukštyn", + "extendUBottom": "pratęsti U žemyn", + "extendVUp": "pratęsti V aukštyn", + "extendVBottom": "pratęsti V žemyn", + "nrHexagonsInHeight": "šešiakampių sk. aukštyje", + "nrHexagonsInWidth": "šešiakampių sk. plotyje", + "bitbybit.vector.length": "vektoriaus ilgis", + "bitbybit.vector.length_description": "Apskaičiuoja vektoriaus ilgį", + "bitbybit.point.maxFilletRadius": "maks. suapvalinimo spindulys", + "maxFilletRadius": "maks. suapvalinimo spindulys", + "bitbybit.point.maxFilletRadius_description": "Apskaičiuoja didžiausią įmanomą suapvalinimo spindulį kampe, sudarytame iš dviejų linijų atkarpų, turinčių bendrą galinį tašką (C), taip, kad suapvalinimo lankas liestųsi su abiem atkarpom ir visas būtų jų viduje.", + "bitbybit.point.maxFilletRadiusHalfLine": "maks. suapvalinimo spindulys pagal pusės atkarpos taisyklę", + "maxFilletRadiusHalfLine": "maks. suapvalinimo spindulys pagal pusės atkarpos taisyklę", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Apskaičiuoja didžiausią įmanomą suapvalinimo spindulį kampe C taip, kad suapvalinimo lankas liestųsi su abiem atkarpomis (P1-C, P2-C), o lietimosi taškai būtų pirmoje kiekvienos atkarpos pusėje (matuojant nuo C).", + "bitbybit.point.maxFilletsHalfLine": "maks. suapvalinimai pagal pusės atkarpos taisyklę", + "maxFilletsHalfLine": "maks. suapvalinimai pagal pusės atkarpos taisyklę", + "bitbybit.point.maxFilletsHalfLine_description": "Apskaičiuoja didžiausią įmanomą suapvalinimo spindulį kiekvienam polilinijos kampui, sudarytam iš taškų sekos. Suapvalinimo spindulys skaičiuojamas kiekvienam vidiniam kampui ir, pasirinktinai, uždarantiems kampams, jei polilinija yra uždara.", + "checkLastWithFirst": "tikrinti paskutinį su pirmu", + "bitbybit.point.safestPointsMaxFilletHalfLine": "saugiausias maks. suapvalinimas taškams pagal pusės atkarpos taisyklę", + "safestPointsMaxFilletHalfLine": "saugiausias maks. suapvalinimas taškams pagal pusės atkarpos taisyklę", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Apskaičiuoja vieną saugiausią maksimalų suapvalinimo spindulį, kurį galima vienodai taikyti visiems taškų rinkinio kampams, remiantis 'pusės atkarpos' taisykle. Jis nustatomas surandant mažiausią iš maksimalių galimų suapvalinimo spindulių, apskaičiuotų kiekvienam atskiram kampui.", + "bitbybit.polyline.maxFilletsHalfLine": "maks. suapvalinimai pagal pusės atkarpos taisyklę", + "bitbybit.polyline.maxFilletsHalfLine_description": "Apskaičiuoja didžiausią įmanomą suapvalinimo spindulį pagal pusės atkarpos taisyklę kiekvienam nurodytos polilinijos kampui. Uždaroje polilinijoje įtraukiami kampai, jungiantys paskutinę atkarpą su pirmąja. Skaičiavime naudojama 'pusės atkarpos' taisyklė, reiškianti, kad suapvalinimo lietimosi taškai turi būti pirmoje kiekvienos su kampu susijusios atkarpos pusėje.", + "bitbybit.polyline.safestFilletRadius": "saugiausias suapvalinimo spindulys", + "safestFilletRadius": "saugiausias suapvalinimo spindulys", + "bitbybit.polyline.safestFilletRadius_description": "Apskaičiuoja vieną saugiausią maksimalų suapvalinimo spindulį, kurį galima vienodai taikyti visiems polilinijos kampams, remiantis 'pusės atkarpos' taisykle. Jis nustatomas surandant mažiausią iš maksimalių galimų suapvalinimo spindulių, apskaičiuotų kiekvienam atskiram kampui.", + "flatTop": "plokščia viršūnė", + "bitbybit.mesh.meshMeshIntersectionPoints": "tinklų sankirtos taškai", + "meshMeshIntersectionPoints": "tinklų sankirtos taškai", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Apskaičiuoja dviejų tinklų sankirtos taškus.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "šešiakampiai tinklelyje", + "hexagonsInGrid": "šešiakampiai tinklelyje", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Sukuria OpenCascade šešiakampių kontūrus tinklelyje", + "scalePatternWidth": "masteliuoti šablono plotį", + "scalePatternHeight": "masteliuoti šablono aukštį", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "tinklų sankirtos kontūrai", + "meshMeshIntersectionWires": "tinklų sankirtos kontūrai", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Atlieka tinklų sankirtos operaciją tarp dviejų formų - abi formos gali turėti savo tinklo sudarymo tikslumą. Šis algoritmas suranda tinklų sankirtą ir grąžina sankirtos kontūrus, kurie yra polilinijos arba daugiakampiai.", + "mesh based": "pagrįsta tinklu", + "precision1": "tikslumas 1", + "precision2": "tikslumas 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "tinklų sankirtos taškai", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Atlieka tinklų sankirtos operaciją tarp dviejų formų - abi formos gali turėti savo tinklo sudarymo tikslumą. Šis algoritmas suranda tinklų sankirtą ir grąžina sankirtos taškus.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "formų tinklų sankirta į kontūrus", + "meshMeshIntersectionOfShapesWires": "formų tinklų sankirta į kontūrus", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Atlieka tinklų sankirtos operaciją tarp vienos formos ir kelių kitų formų - visos formos gali turėti savo tinklo sudarymo tikslumą. Šis algoritmas suranda tinklų sankirtą ir grąžina sankirtos kontūrus, kurie yra polilinijos arba daugiakampiai.", + "precisionShapes": "formų tikslumas", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "formų tinklų sankirta į taškus", + "meshMeshIntersectionOfShapesPoints": "formų tinklų sankirta į taškus", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Atlieka tinklų sankirtos operaciją tarp vienos formos ir kelių kitų formų - visos formos gali turėti savo tinklo sudarymo tikslumą. Šis algoritmas suranda tinklų sankirtą ir grąžina sankirtos taškus.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "šešiakampiai tinklelyje", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Sukuria OpenCascade šešiakampius tinklelyje", + "bitbybit.jscad.toPolygonPoints": "į daugiakampio taškus", + "toPolygonPoints": "į daugiakampio taškus", + "bitbybit.jscad.toPolygonPoints_description": "Paverčia jscad formą į daugiakampio taškų rinkinį, reprezentuojantį tinklą", + "conversions": "konvertavimai", + "bitbybit.manifold.toPolygonPoints": "į daugiakampio taškus", + "bitbybit.manifold.toPolygonPoints_description": "Paverčia manifold formą į daugiakampio taškų rinkinį, reprezentuojantį tinklą.", + "flatU": "plokščias U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "suskirstyti į šešiakampes skyles", + "subdivideToHexagonHoles": "suskirstyti į šešiakampes skyles", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Suskirsto paviršių į šešiakampes skyles", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "konvertuoti linijas į NURBS kreives", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Konvertuoja linijas į NURBS kreives. Grąžina verbnurbs Line objektų masyvą.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "konvertuoti liniją į NURBS kreivę", + "convertLineToNurbsCurve": "konvertuoti liniją į NURBS kreivę", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Konvertuoja liniją į NURBS kreivę. Grąžina verbnurbs Line objektą.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "konvertuoti poliliniją į NURBS kreivę", + "convertPolylineToNurbsCurve": "konvertuoti poliliniją į NURBS kreivę", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Konvertuoja poliliniją į NURBS kreivę. Grąžina verbnurbs NurbsCurve objektą.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "konvertuoti polilinijas į NURBS kreives", + "convertPolylinesToNurbsCurves": "konvertuoti polilinijas į NURBS kreives", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Konvertuoja polilinijas į NURBS kreives. Grąžina verbnurbs NurbsCurve objektus.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/pt.json b/languages/pt.json index d0b07c8c..d82b6ff0 100644 --- a/languages/pt.json +++ b/languages/pt.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Cria malha a partir da forma", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "a partir de pontos do polígono", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Cria um Manifold a partir de um conjunto de pontos do polígono que descrevem triângulos.", - "traingle": "triângulo" + "traingle": "triângulo", + "bitbybit.point.stretchPointsDirFromCenter": "esticar pontos na direção a partir do centro", + "stretchPointsDirFromCenter": "esticar pontos na direção a partir do centro", + "bitbybit.point.stretchPointsDirFromCenter_description": "Estica múltiplos pontos fornecendo ponto central, direção e fator de escala uniforme", + "bitbybit.point.hexGridScaledToFit": "grade hexagonal escalada para ajustar", + "hexGridScaledToFit": "grade hexagonal escalada para ajustar", + "bitbybit.point.hexGridScaledToFit_description": "Cria uma grade hexagonal de topo pontiagudo, escalando os hexágonos para ajustar exatamente às dimensões especificadas. Retorna os pontos centrais e os vértices de cada hexágono (potencialmente escalado). Os hexágonos são ordenados primeiro por coluna, depois por linha.", + "nrHexagonsU": "nº hexágonos U", + "nrHexagonsV": "nº hexágonos V", + "extendTop": "estender topo", + "extendBottom": "estender base", + "extendLeft": "estender esquerda", + "extendRight": "estender direita", + "centerGrid": "centralizar grade", + "bitbybit.point.sortPoints": "ordenar pontos", + "sortPoints": "ordenar pontos", + "bitbybit.point.sortPoints_description": "Ordena pontos lexicograficamente (X, depois Y, depois Z)", + "bitbybit.line.lineLineIntersection": "interseção linha-linha", + "lineLineIntersection": "interseção linha-linha", + "bitbybit.line.lineLineIntersection_description": "Se duas linhas se cruzarem, retorna o ponto de interseção", + "line1": "linha 1", + "line2": "linha 2", + "checkSegmentsOnly": "verificar apenas segmentos", + "bitbybit.polyline.polylineToLines": "polilinha para linhas", + "polylineToLines": "polilinha para linhas", + "bitbybit.polyline.polylineToLines_description": "Cria as linhas a partir da polilinha", + "bitbybit.polyline.polylineToSegments": "polilinha para segmentos", + "polylineToSegments": "polilinha para segmentos", + "bitbybit.polyline.polylineToSegments_description": "Cria os segmentos a partir da polilinha", + "bitbybit.polyline.polylineSelfIntersection": "autointerseção de polilinha", + "polylineSelfIntersection": "autointerseção de polilinha", + "bitbybit.polyline.polylineSelfIntersection_description": "Encontra os pontos de autointerseção da polilinha", + "bitbybit.polyline.twoPolylineIntersection": "interseção de duas polilinhas", + "twoPolylineIntersection": "interseção de duas polilinhas", + "bitbybit.polyline.twoPolylineIntersection_description": "Encontra os pontos de interseção entre duas polilinhas.", + "polyline1": "polilinha 1", + "polyline2": "polilinha 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "subdividir em arames hexagonais", + "subdivideToHexagonWires": "subdividir em arames hexagonais", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Subdivide uma face em arames hexagonais", + "extendUUp": "estender U acima", + "extendUBottom": "estender U abaixo", + "extendVUp": "estender V acima", + "extendVBottom": "estender V abaixo", + "nrHexagonsInHeight": "nº hexágonos altura", + "nrHexagonsInWidth": "nº hexágonos largura", + "bitbybit.vector.length": "comprimento do vetor", + "bitbybit.vector.length_description": "Calcula o comprimento do vetor", + "bitbybit.point.maxFilletRadius": "raio máximo de fillet", + "maxFilletRadius": "raio máximo de fillet", + "bitbybit.point.maxFilletRadius_description": "Calcula o raio máximo de concordância (fillet) possível num canto formado por dois segmentos de linha que partilham um ponto extremo (C), de tal forma que o arco de concordância seja tangente a ambos os segmentos e esteja inteiramente contido neles.", + "bitbybit.point.maxFilletRadiusHalfLine": "raio máximo de fillet meia-linha", + "maxFilletRadiusHalfLine": "raio máximo de fillet meia-linha", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Calcula o raio máximo de concordância (fillet) possível num canto C, de tal forma que o arco de concordância seja tangente a ambos os segmentos (P1-C, P2-C) e os pontos tangentes estejam dentro da primeira metade de cada segmento (medido a partir de C).", + "bitbybit.point.maxFilletsHalfLine": "fillets máximos meia-linha", + "maxFilletsHalfLine": "fillets máximos meia-linha", + "bitbybit.point.maxFilletsHalfLine_description": "Calcula o raio máximo de concordância (fillet) possível em cada canto de uma polilinha formada por uma série de pontos. O raio de concordância é calculado para cada canto interno e opcionalmente para os cantos de fecho se a polilinha for fechada.", + "checkLastWithFirst": "verificar último com primeiro", + "bitbybit.point.safestPointsMaxFilletHalfLine": "raio máximo de fillet mais seguro (pontos, meia-linha)", + "safestPointsMaxFilletHalfLine": "raio máximo de fillet mais seguro (pontos, meia-linha)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Calcula o único raio máximo de concordância (fillet) mais seguro que pode ser aplicado uniformemente a todos os cantos de uma coleção de pontos, com base na restrição de 'meia-linha'. Isso é determinado encontrando o mínimo dos raios máximos de concordância possíveis calculados para cada canto individual.", + "bitbybit.polyline.maxFilletsHalfLine": "fillets máximos meia-linha", + "bitbybit.polyline.maxFilletsHalfLine_description": "Calcula o raio máximo de concordância (fillet) de 'meia-linha' possível para cada canto de uma dada polilinha. Para uma polilinha fechada, inclui os cantos que conectam o último segmento de volta ao primeiro. O cálculo usa a restrição de 'meia-linha', significando que os pontos tangentes do fillet devem estar dentro da primeira metade de cada segmento conectado ao canto.", + "bitbybit.polyline.safestFilletRadius": "raio de fillet mais seguro", + "safestFilletRadius": "raio de fillet mais seguro", + "bitbybit.polyline.safestFilletRadius_description": "Calcula o único raio máximo de concordância (fillet) mais seguro que pode ser aplicado uniformemente a todos os cantos de uma polilinha, com base na restrição de 'meia-linha'. Isso é determinado encontrando o mínimo dos raios máximos de concordância possíveis calculados para cada canto individual.", + "flatTop": "topo plano", + "bitbybit.mesh.meshMeshIntersectionPoints": "pontos de interseção malha-malha", + "meshMeshIntersectionPoints": "pontos de interseção malha-malha", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Calcula os pontos de interseção de duas malhas.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "hexágonos na grade", + "hexagonsInGrid": "hexágonos na grade", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Cria arames hexagonais OpenCascade na grade", + "scalePatternWidth": "escalar largura do padrão", + "scalePatternHeight": "escalar altura do padrão", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "arames de interseção malha-malha", + "meshMeshIntersectionWires": "arames de interseção malha-malha", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Realiza a operação de interseção malha-malha entre duas formas - ambas as formas podem ter sua própria precisão de malhagem. Este algoritmo cruza as malhas e retorna os arames da interseção, que são polilinhas ou polígonos.", + "mesh based": "baseado em malha", + "precision1": "precisão 1", + "precision2": "precisão 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "pontos de interseção malha-malha", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Realiza a operação de interseção malha-malha entre duas formas - ambas as formas podem ter sua própria precisão de malhagem. Este algoritmo cruza as malhas e retorna os pontos da interseção.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "interseção malha-malha para arames", + "meshMeshIntersectionOfShapesWires": "interseção malha-malha para arames", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Realiza a operação de interseção malha-malha entre a forma e múltiplas outras formas - todas as formas podem ter sua própria precisão de malhagem. Este algoritmo cruza as malhas e retorna os arames da interseção, que são polilinhas ou polígonos.", + "precisionShapes": "precisão formas", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "interseção malha-malha para pontos", + "meshMeshIntersectionOfShapesPoints": "interseção malha-malha para pontos", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Realiza a operação de interseção malha-malha entre a forma e múltiplas outras formas - todas as formas podem ter sua própria precisão de malhagem. Este algoritmo cruza as malhas e retorna os pontos da interseção.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "hexágonos na grade", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Cria hexágonos OpenCascade na grade (faces)", + "bitbybit.jscad.toPolygonPoints": "para pontos de polígono", + "toPolygonPoints": "para pontos de polígono", + "bitbybit.jscad.toPolygonPoints_description": "Transforma a forma jscad numa coleção de pontos de polígono representando a malha", + "conversions": "conversões", + "bitbybit.manifold.toPolygonPoints": "para pontos de polígono", + "bitbybit.manifold.toPolygonPoints_description": "Transforma a forma manifold numa coleção de pontos de polígono representando a malha.", + "flatU": "plano U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "subdividir em furos hexagonais", + "subdivideToHexagonHoles": "subdividir em furos hexagonais", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Subdivide uma face em furos hexagonais", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "converter linhas para curvas NURBS", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Converte linhas em curvas NURBS. Retorna um array de objetos verbnurbs Line.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "converter linha para curva NURBS", + "convertLineToNurbsCurve": "converter linha para curva NURBS", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Converte linha em curva NURBS. Retorna o objeto verbnurbs Line.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "converter polilinha para curva NURBS", + "convertPolylineToNurbsCurve": "converter polilinha para curva NURBS", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Converte uma polilinha em uma curva NURBS. Retorna o objeto verbnurbs NurbsCurve.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "converter polilinhas para curvas NURBS", + "convertPolylinesToNurbsCurves": "converter polilinhas para curvas NURBS", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Converte polilinhas em curvas NURBS. Retorna os objetos verbnurbs NurbsCurve.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/ru.json b/languages/ru.json index e94ae36c..290ba8e0 100644 --- a/languages/ru.json +++ b/languages/ru.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Создает сетку из формы", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "из точек полигона", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Создать многообразие (Manifold) из набора точек полигона, описывающих треугольники.", - "traingle": "треугольник" + "traingle": "треугольник", + "bitbybit.point.stretchPointsDirFromCenter": "растянуть точки в направлении от центра", + "stretchPointsDirFromCenter": "растянуть точки в направлении от центра", + "bitbybit.point.stretchPointsDirFromCenter_description": "Растягивает несколько точек, задавая центральную точку, направление и единый коэффициент масштабирования", + "bitbybit.point.hexGridScaledToFit": "шестиугольная сетка, масштабированная по размеру", + "hexGridScaledToFit": "шестиугольная сетка, масштабированная по размеру", + "bitbybit.point.hexGridScaledToFit_description": "Создает шестиугольную сетку с острым верхом, масштабируя шестиугольники точно под указанные размеры. Возвращает как центральные точки, так и вершины каждого (потенциально масштабированного) шестиугольника. Шестиугольники упорядочены сначала по столбцам, затем по строкам.", + "nrHexagonsU": "кол-во шестиугольников U", + "nrHexagonsV": "кол-во шестиугольников V", + "extendTop": "расширить сверху", + "extendBottom": "расширить снизу", + "extendLeft": "расширить слева", + "extendRight": "расширить справа", + "centerGrid": "центрировать сетку", + "bitbybit.point.sortPoints": "сортировать точки", + "sortPoints": "сортировать точки", + "bitbybit.point.sortPoints_description": "Сортирует точки лексикографически (X, затем Y, затем Z)", + "bitbybit.line.lineLineIntersection": "пересечение линия-линия", + "lineLineIntersection": "пересечение линия-линия", + "bitbybit.line.lineLineIntersection_description": "Если две линии пересекаются, вернуть точку пересечения", + "line1": "линия 1", + "line2": "линия 2", + "checkSegmentsOnly": "проверять только отрезки", + "bitbybit.polyline.polylineToLines": "полилиния в линии", + "polylineToLines": "полилиния в линии", + "bitbybit.polyline.polylineToLines_description": "Создать линии из полилинии", + "bitbybit.polyline.polylineToSegments": "полилиния в отрезки", + "polylineToSegments": "полилиния в отрезки", + "bitbybit.polyline.polylineToSegments_description": "Создать отрезки из полилинии", + "bitbybit.polyline.polylineSelfIntersection": "самопересечение полилинии", + "polylineSelfIntersection": "самопересечение полилинии", + "bitbybit.polyline.polylineSelfIntersection_description": "Находит точки самопересечения полилинии", + "bitbybit.polyline.twoPolylineIntersection": "пересечение двух полилиний", + "twoPolylineIntersection": "пересечение двух полилиний", + "bitbybit.polyline.twoPolylineIntersection_description": "Находит точки пересечения между двумя полилиниями.", + "polyline1": "полилиния 1", + "polyline2": "полилиния 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "разделить на шестиугольные каркасы", + "subdivideToHexagonWires": "разделить на шестиугольные каркасы", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Разделяет грань на шестиугольные каркасы", + "extendUUp": "расширить U вверх", + "extendUBottom": "расширить U вниз", + "extendVUp": "расширить V вверх", + "extendVBottom": "расширить V вниз", + "nrHexagonsInHeight": "кол-во шестиугольников по высоте", + "nrHexagonsInWidth": "кол-во шестиугольников по ширине", + "bitbybit.vector.length": "длина вектора", + "bitbybit.vector.length_description": "Вычисляет длину вектора", + "bitbybit.point.maxFilletRadius": "макс. радиус скругления", + "maxFilletRadius": "макс. радиус скругления", + "bitbybit.point.maxFilletRadius_description": "Вычисляет максимально возможный радиус скругления в углу, образованном двумя отрезками линий с общей конечной точкой (C), так, чтобы дуга скругления была касательной к обоим отрезкам и полностью лежала внутри них.", + "bitbybit.point.maxFilletRadiusHalfLine": "макс. радиус скругления (половина линии)", + "maxFilletRadiusHalfLine": "макс. радиус скругления (половина линии)", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Вычисляет максимально возможный радиус скругления в углу C так, чтобы дуга скругления была касательной к обоим отрезкам (P1-C, P2-C), а точки касания лежали в пределах первой половины каждого отрезка (измеренной от C).", + "bitbybit.point.maxFilletsHalfLine": "макс. скругления (половина линии)", + "maxFilletsHalfLine": "макс. скругления (половина линии)", + "bitbybit.point.maxFilletsHalfLine_description": "Вычисляет максимально возможный радиус скругления в каждом углу полилинии, образованной серией точек. Радиус скругления рассчитывается для каждого внутреннего угла и опционально для замыкающих углов, если полилиния замкнута.", + "checkLastWithFirst": "проверить последний с первым", + "bitbybit.point.safestPointsMaxFilletHalfLine": "наиболее безопасный макс. радиус скругления для точек (половина линии)", + "safestPointsMaxFilletHalfLine": "наиболее безопасный макс. радиус скругления для точек (половина линии)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Вычисляет единственный наиболее безопасный максимальный радиус скругления, который может быть равномерно применен ко всем углам набора точек, на основе ограничения 'половина линии'. Он определяется путем нахождения минимума из максимально возможных радиусов скругления, рассчитанных для каждого отдельного угла.", + "bitbybit.polyline.maxFilletsHalfLine": "макс. скругления (половина линии)", + "bitbybit.polyline.maxFilletsHalfLine_description": "Вычисляет максимально возможный радиус скругления по правилу 'половина линии' для каждого угла данной полилинии. Для замкнутой полилинии он включает углы, соединяющие последний отрезок с первым. Расчет использует ограничение 'половина линии', что означает, что точки касания скругления должны лежать в пределах первой половины каждого отрезка, соединенного с углом.", + "bitbybit.polyline.safestFilletRadius": "наиболее безопасный радиус скругления", + "safestFilletRadius": "наиболее безопасный радиус скругления", + "bitbybit.polyline.safestFilletRadius_description": "Вычисляет единственный наиболее безопасный максимальный радиус скругления, который может быть равномерно применен ко всем углам полилинии, на основе ограничения 'половина линии'. Он определяется путем нахождения минимума из максимально возможных радиусов скругления, рассчитанных для каждого отдельного угла.", + "flatTop": "плоская вершина", + "bitbybit.mesh.meshMeshIntersectionPoints": "точки пересечения сетка-сетка", + "meshMeshIntersectionPoints": "точки пересечения сетка-сетка", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Вычисляет точки пересечения двух сеток.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "шестиугольники в сетке", + "hexagonsInGrid": "шестиугольники в сетке", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Создает шестиугольные каркасы OpenCascade в сетке", + "scalePatternWidth": "масштабировать ширину узора", + "scalePatternHeight": "масштабировать высоту узора", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "каркасы пересечения сетка-сетка", + "meshMeshIntersectionWires": "каркасы пересечения сетка-сетка", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Выполняет операцию пересечения сетка-сетка между двумя формами - обе формы могут иметь свою точность построения сетки. Этот алгоритм пересекает сетки и возвращает каркасы пересечения, которые являются полилиниями или многоугольниками.", + "mesh based": "на основе сетки", + "precision1": "точность 1", + "precision2": "точность 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "точки пересечения сетка-сетка", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Выполняет операцию пересечения сетка-сетка между двумя формами - обе формы могут иметь свою точность построения сетки. Этот алгоритм пересекает сетки и возвращает точки пересечения.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "пересечение сетка-сетка в каркасы", + "meshMeshIntersectionOfShapesWires": "пересечение сетка-сетка в каркасы", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Выполняет операцию пересечения сетка-сетка между формой и несколькими другими формами - все формы могут иметь свою точность построения сетки. Этот алгоритм пересекает сетки и возвращает каркасы пересечения, которые являются полилиниями или многоугольниками.", + "precisionShapes": "точность форм", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "пересечение сетка-сетка в точки", + "meshMeshIntersectionOfShapesPoints": "пересечение сетка-сетка в точки", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Выполняет операцию пересечения сетка-сетка между формой и несколькими другими формами - все формы могут иметь свою точность построения сетки. Этот алгоритм пересекает сетки и возвращает точки пересечения.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "шестиугольники в сетке", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Создает шестиугольники OpenCascade в сетке (как грани)", + "bitbybit.jscad.toPolygonPoints": "в точки полигона", + "toPolygonPoints": "в точки полигона", + "bitbybit.jscad.toPolygonPoints_description": "Преобразует форму jscad в набор точек полигонов, представляющих сетку", + "conversions": "преобразования", + "bitbybit.manifold.toPolygonPoints": "в точки полигона", + "bitbybit.manifold.toPolygonPoints_description": "Преобразует форму manifold в набор точек полигонов, представляющих сетку.", + "flatU": "плоский U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "разделить на шестиугольные отверстия", + "subdivideToHexagonHoles": "разделить на шестиугольные отверстия", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Разделяет грань на шестиугольные отверстия", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "конвертировать линии в NURBS кривые", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Конвертирует линии в NURBS кривые. Возвращает массив объектов verbnurbs Line.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "конвертировать линию в NURBS кривую", + "convertLineToNurbsCurve": "конвертировать линию в NURBS кривую", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Конвертирует линию в NURBS кривую. Возвращает объект verbnurbs Line.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "конвертировать полилинию в NURBS кривую", + "convertPolylineToNurbsCurve": "конвертировать полилинию в NURBS кривую", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Конвертирует полилинию в NURBS кривую. Возвращает объект verbnurbs NurbsCurve.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "конвертировать полилинии в NURBS кривые", + "convertPolylinesToNurbsCurves": "конвертировать полилинии в NURBS кривые", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Конвертирует полилинии в NURBS кривые. Возвращает объекты verbnurbs NurbsCurve.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/uk.json b/languages/uk.json index b1684379..ee56f75d 100644 --- a/languages/uk.json +++ b/languages/uk.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "Створює сітку з форми", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "з точок полігону", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "Створити многовид (Manifold) із набору точок полігону, що описують трикутники.", - "traingle": "трикутник" + "traingle": "трикутник", + "bitbybit.point.stretchPointsDirFromCenter": "розтягнути точки в напрямку від центру", + "stretchPointsDirFromCenter": "розтягнути точки в напрямку від центру", + "bitbybit.point.stretchPointsDirFromCenter_description": "Розтягує кілька точок, вказавши центральну точку, напрямок та єдиний коефіцієнт масштабування", + "bitbybit.point.hexGridScaledToFit": "шестикутна сітка, масштабована до розміру", + "hexGridScaledToFit": "шестикутна сітка, масштабована до розміру", + "bitbybit.point.hexGridScaledToFit_description": "Створює шестикутну сітку з гострим верхом, масштабуючи шестикутники точно до вказаних розмірів. Повертає як центральні точки, так і вершини кожного (потенційно масштабованого) шестикутника. Шестикутники впорядковані спочатку за стовпцями, потім за рядками.", + "nrHexagonsU": "кількість шестикутників U", + "nrHexagonsV": "кількість шестикутників V", + "extendTop": "розширити зверху", + "extendBottom": "розширити знизу", + "extendLeft": "розширити зліва", + "extendRight": "розширити справа", + "centerGrid": "центрувати сітку", + "bitbybit.point.sortPoints": "сортувати точки", + "sortPoints": "сортувати точки", + "bitbybit.point.sortPoints_description": "Сортує точки лексикографічно (X, потім Y, потім Z)", + "bitbybit.line.lineLineIntersection": "перетин лінія-лінія", + "lineLineIntersection": "перетин лінія-лінія", + "bitbybit.line.lineLineIntersection_description": "Якщо дві лінії перетинаються, повернути точку перетину", + "line1": "лінія 1", + "line2": "лінія 2", + "checkSegmentsOnly": "перевіряти лише відрізки", + "bitbybit.polyline.polylineToLines": "полілінія в лінії", + "polylineToLines": "полілінія в лінії", + "bitbybit.polyline.polylineToLines_description": "Створити лінії з полілінії", + "bitbybit.polyline.polylineToSegments": "полілінія в відрізки", + "polylineToSegments": "полілінія в відрізки", + "bitbybit.polyline.polylineToSegments_description": "Створити відрізки з полілінії", + "bitbybit.polyline.polylineSelfIntersection": "самоперетин полілінії", + "polylineSelfIntersection": "самоперетин полілінії", + "bitbybit.polyline.polylineSelfIntersection_description": "Знаходить точки самоперетину полілінії", + "bitbybit.polyline.twoPolylineIntersection": "перетин двох поліліній", + "twoPolylineIntersection": "перетин двох поліліній", + "bitbybit.polyline.twoPolylineIntersection_description": "Знаходить точки перетину між двома полілініями.", + "polyline1": "полілінія 1", + "polyline2": "полілінія 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "розділити на шестикутні контури", + "subdivideToHexagonWires": "розділити на шестикутні контури", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "Розділяє грань на шестикутні контури", + "extendUUp": "розширити U вгору", + "extendUBottom": "розширити U вниз", + "extendVUp": "розширити V вгору", + "extendVBottom": "розширити V вниз", + "nrHexagonsInHeight": "кількість шестикутників у висоту", + "nrHexagonsInWidth": "кількість шестикутників у ширину", + "bitbybit.vector.length": "довжина вектора", + "bitbybit.vector.length_description": "Обчислює довжину вектора", + "bitbybit.point.maxFilletRadius": "макс. радіус скруглення", + "maxFilletRadius": "макс. радіус скруглення", + "bitbybit.point.maxFilletRadius_description": "Обчислює максимально можливий радіус скруглення в куті, утвореному двома відрізками ліній, що мають спільну кінцеву точку (C), таким чином, щоб дуга скруглення була дотичною до обох відрізків і повністю лежала в їх межах.", + "bitbybit.point.maxFilletRadiusHalfLine": "макс. радіус скруглення (половина лінії)", + "maxFilletRadiusHalfLine": "макс. радіус скруглення (половина лінії)", + "bitbybit.point.maxFilletRadiusHalfLine_description": "Обчислює максимально можливий радіус скруглення в куті C так, щоб дуга скруглення була дотичною до обох відрізків (P1-C, P2-C), а точки дотику лежали в межах першої половини кожного відрізка (виміряно від C).", + "bitbybit.point.maxFilletsHalfLine": "макс. скруглення (половина лінії)", + "maxFilletsHalfLine": "макс. скруглення (половина лінії)", + "bitbybit.point.maxFilletsHalfLine_description": "Обчислює максимально можливий радіус скруглення в кожному куті полілінії, утвореної послідовністю точок. Радіус скруглення обчислюється для кожного внутрішнього кута та, за бажанням, для кутів замикання, якщо полілінія замкнена.", + "checkLastWithFirst": "перевірити останній з першим", + "bitbybit.point.safestPointsMaxFilletHalfLine": "найбезпечніший макс. радіус скруглення для точок (половина лінії)", + "safestPointsMaxFilletHalfLine": "найбезпечніший макс. радіус скруглення для точок (половина лінії)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "Обчислює єдиний найбезпечніший максимальний радіус скруглення, який можна рівномірно застосувати до всіх кутів набору точок, на основі обмеження 'половина лінії'. Це визначається шляхом знаходження мінімуму з максимально можливих радіусів скруглення, обчислених для кожного окремого кута.", + "bitbybit.polyline.maxFilletsHalfLine": "макс. скруглення (половина лінії)", + "bitbybit.polyline.maxFilletsHalfLine_description": "Обчислює максимально можливий радіус скруглення за правилом 'половина лінії' для кожного кута даної полілінії. Для замкненої полілінії він включає кути, що з'єднують останній відрізок з першим. Розрахунок використовує обмеження 'половина лінії', що означає, що точки дотику скруглення повинні лежати в межах першої половини кожного відрізка, з'єднаного з кутом.", + "bitbybit.polyline.safestFilletRadius": "найбезпечніший радіус скруглення", + "safestFilletRadius": "найбезпечніший радіус скруглення", + "bitbybit.polyline.safestFilletRadius_description": "Обчислює єдиний найбезпечніший максимальний радіус скруглення, який можна рівномірно застосувати до всіх кутів полілінії, на основі обмеження 'половина лінії'. Це визначається шляхом знаходження мінімуму з максимально можливих радіусів скруглення, обчислених для кожного окремого кута.", + "flatTop": "пласка вершина", + "bitbybit.mesh.meshMeshIntersectionPoints": "точки перетину сітка-сітка", + "meshMeshIntersectionPoints": "точки перетину сітка-сітка", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "Обчислює точки перетину двох сіток.", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "шестикутники в сітці", + "hexagonsInGrid": "шестикутники в сітці", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "Створює шестикутні контури OpenCascade у сітці", + "scalePatternWidth": "масштабувати ширину візерунка", + "scalePatternHeight": "масштабувати висоту візерунка", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "контури перетину сітка-сітка", + "meshMeshIntersectionWires": "контури перетину сітка-сітка", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "Виконує операцію перетину сітка-сітка між двома формами - обидві форми можуть мати власну точність розбиття на сітку. Цей алгоритм перетинає сітки та повертає контури перетину, які є полілініями або багатокутниками.", + "mesh based": "на основі сітки", + "precision1": "точність 1", + "precision2": "точність 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "точки перетину сітка-сітка", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "Виконує операцію перетину сітка-сітка між двома формами - обидві форми можуть мати власну точність розбиття на сітку. Цей алгоритм перетинає сітки та повертає точки перетину.", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "перетин сітка-сітка в контури", + "meshMeshIntersectionOfShapesWires": "перетин сітка-сітка в контури", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "Виконує операцію перетину сітка-сітка між формою та кількома іншими формами - усі форми можуть мати власну точність розбиття на сітку. Цей алгоритм перетинає сітки та повертає контури перетину, які є полілініями або багатокутниками.", + "precisionShapes": "точність форм", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "перетин сітка-сітка в точки", + "meshMeshIntersectionOfShapesPoints": "перетин сітка-сітка в точки", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "Виконує операцію перетину сітка-сітка між формою та кількома іншими формами - усі форми можуть мати власну точність розбиття на сітку. Цей алгоритм перетинає сітки та повертає точки перетину.", + "bitbybit.occt.shapes.face.hexagonsInGrid": "шестикутники в сітці", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "Створює шестикутники OpenCascade у сітці (грані)", + "bitbybit.jscad.toPolygonPoints": "в точки полігону", + "toPolygonPoints": "в точки полігону", + "bitbybit.jscad.toPolygonPoints_description": "Перетворює фігуру jscad на колекцію точок полігону, що представляють сітку", + "conversions": "перетворення", + "bitbybit.manifold.toPolygonPoints": "в точки полігону", + "bitbybit.manifold.toPolygonPoints_description": "Перетворює фігуру manifold на колекцію точок полігону, що представляють сітку.", + "flatU": "плаский U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "розділити на шестикутні отвори", + "subdivideToHexagonHoles": "розділити на шестикутні отвори", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "Розділяє грань на шестикутні отвори", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "конвертувати лінії в NURBS криві", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "Конвертує лінії в NURBS криві. Повертає масив об'єктів verbnurbs Line.", + "bitbybit.verb.curve.convertLineToNurbsCurve": "конвертувати лінію в NURBS криву", + "convertLineToNurbsCurve": "конвертувати лінію в NURBS криву", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "Конвертує лінію в NURBS криву. Повертає об'єкт verbnurbs Line.", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "конвертувати полілінію в NURBS криву", + "convertPolylineToNurbsCurve": "конвертувати полілінію в NURBS криву", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "Конвертує полілінію в NURBS криву. Повертає об'єкт verbnurbs NurbsCurve.", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "конвертувати полілінії в NURBS криві", + "convertPolylinesToNurbsCurves": "конвертувати полілінії в NURBS криві", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "Конвертує полілінії в NURBS криві. Повертає об'єкти verbnurbs NurbsCurve.", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/languages/zh-hans.json b/languages/zh-hans.json index d2c9d0ab..a213aef9 100644 --- a/languages/zh-hans.json +++ b/languages/zh-hans.json @@ -5192,5 +5192,120 @@ "bitbybit.occt.shapesToMeshes_description": "从形状创建网格", "bitbybit.manifold.manifold.shapes.fromPolygonPoints": "从多边形点", "bitbybit.manifold.manifold.shapes.fromPolygonPoints_description": "从描述三角形的一组多边形点创建流形(Manifold)。", - "traingle": "三角形" + "traingle": "三角形", + "bitbybit.point.stretchPointsDirFromCenter": "从中心沿方向拉伸点", + "stretchPointsDirFromCenter": "从中心沿方向拉伸点", + "bitbybit.point.stretchPointsDirFromCenter_description": "通过提供中心点、方向和统一缩放因子来拉伸多个点", + "bitbybit.point.hexGridScaledToFit": "缩放以适应的六边形网格", + "hexGridScaledToFit": "缩放以适应的六边形网格", + "bitbybit.point.hexGridScaledToFit_description": "创建一个尖顶六边形网格,缩放六边形以精确适应指定的尺寸。返回每个(可能已缩放的)六边形的中心点和顶点。六边形按先列后行的顺序排列。", + "nrHexagonsU": "六边形数 U", + "nrHexagonsV": "六边形数 V", + "extendTop": "延伸上", + "extendBottom": "延伸下", + "extendLeft": "延伸左", + "extendRight": "延伸右", + "centerGrid": "居中网格", + "bitbybit.point.sortPoints": "排序点", + "sortPoints": "排序点", + "bitbybit.point.sortPoints_description": "按字典序(先 X,再 Y,后 Z)排序点", + "bitbybit.line.lineLineIntersection": "线线交点", + "lineLineIntersection": "线线交点", + "bitbybit.line.lineLineIntersection_description": "如果两条线相交,则返回交点", + "line1": "线 1", + "line2": "线 2", + "checkSegmentsOnly": "仅检查线段", + "bitbybit.polyline.polylineToLines": "多段线转直线", + "polylineToLines": "多段线转直线", + "bitbybit.polyline.polylineToLines_description": "从多段线创建直线", + "bitbybit.polyline.polylineToSegments": "多段线转线段", + "polylineToSegments": "多段线转线段", + "bitbybit.polyline.polylineToSegments_description": "从多段线创建线段", + "bitbybit.polyline.polylineSelfIntersection": "多段线自相交", + "polylineSelfIntersection": "多段线自相交", + "bitbybit.polyline.polylineSelfIntersection_description": "查找多段线的自相交点", + "bitbybit.polyline.twoPolylineIntersection": "两条多段线相交", + "twoPolylineIntersection": "两条多段线相交", + "bitbybit.polyline.twoPolylineIntersection_description": "查找两条多段线之间的交点。", + "polyline1": "多段线 1", + "polyline2": "多段线 2", + "bitbybit.occt.shapes.face.subdivideToHexagonWires": "细分为六边形线框", + "subdivideToHexagonWires": "细分为六边形线框", + "bitbybit.occt.shapes.face.subdivideToHexagonWires_description": "将面细分为六边形线框", + "extendUUp": "延伸 U 上", + "extendUBottom": "延伸 U 下", + "extendVUp": "延伸 V 上", + "extendVBottom": "延伸 V 下", + "nrHexagonsInHeight": "高方向六边形数", + "nrHexagonsInWidth": "宽方向六边形数", + "bitbybit.vector.length": "向量长度", + "bitbybit.vector.length_description": "计算向量的长度", + "bitbybit.point.maxFilletRadius": "最大圆角半径", + "maxFilletRadius": "最大圆角半径", + "bitbybit.point.maxFilletRadius_description": "计算由共享端点 (C) 的两条线段形成的角点的最大可能圆角半径,使得圆角弧与两条线段相切并完全位于其内部。", + "bitbybit.point.maxFilletRadiusHalfLine": "最大圆角半径(半线约束)", + "maxFilletRadiusHalfLine": "最大圆角半径(半线约束)", + "bitbybit.point.maxFilletRadiusHalfLine_description": "计算角点 C 处的最大可能圆角半径,使得圆角弧与两条线段 (P1-C, P2-C) 相切,并且切点位于每条线段的前半部分(从 C 点测量)。", + "bitbybit.point.maxFilletsHalfLine": "最大圆角(半线约束)", + "maxFilletsHalfLine": "最大圆角(半线约束)", + "bitbybit.point.maxFilletsHalfLine_description": "计算由一系列点形成的多段线每个角点的最大可能圆角半径。为每个内部角点计算圆角半径,如果多段线是闭合的,则可选地为闭合角点计算。", + "checkLastWithFirst": "检查首尾连接", + "bitbybit.point.safestPointsMaxFilletHalfLine": "最安全点最大圆角(半线约束)", + "safestPointsMaxFilletHalfLine": "最安全点最大圆角(半线约束)", + "bitbybit.point.safestPointsMaxFilletHalfLine_description": "基于“半线”约束,计算可统一应用于点集合所有角点的单一最安全最大圆角半径。这通过查找为每个单独角点计算的最大可能圆角半径的最小值来确定。", + "bitbybit.polyline.maxFilletsHalfLine": "最大圆角(半线约束)", + "bitbybit.polyline.maxFilletsHalfLine_description": "计算给定多段线每个角点的最大可能半线圆角半径。对于闭合多段线,它包括连接最后一段到第一段的角点。计算使用“半线”约束,这意味着圆角的切点必须位于连接到角点的每个线段的前半部分内。", + "bitbybit.polyline.safestFilletRadius": "最安全圆角半径", + "safestFilletRadius": "最安全圆角半径", + "bitbybit.polyline.safestFilletRadius_description": "基于“半线”约束,计算可统一应用于多段线所有角点的单一最安全最大圆角半径。这通过查找为每个单独角点计算的最大可能圆角半径的最小值来确定。", + "flatTop": "平顶", + "bitbybit.mesh.meshMeshIntersectionPoints": "网格与网格交点", + "meshMeshIntersectionPoints": "网格与网格交点", + "bitbybit.mesh.meshMeshIntersectionPoints_description": "计算两个网格的交点。", + "bitbybit.occt.shapes.wire.hexagonsInGrid": "网格中的六边形", + "hexagonsInGrid": "网格中的六边形", + "bitbybit.occt.shapes.wire.hexagonsInGrid_description": "在网格中创建 OpenCascade 六边形线框", + "scalePatternWidth": "缩放图案宽", + "scalePatternHeight": "缩放图案高", + "bitbybit.occt.booleans.meshMeshIntersectionWires": "网格与网格相交线框", + "meshMeshIntersectionWires": "网格与网格相交线框", + "bitbybit.occt.booleans.meshMeshIntersectionWires_description": "在两个形状之间执行网格与网格求交操作 - 两个形状都可以有自己的网格划分精度。此算法对网格进行求交,并返回相交部分的线框,这些线框是多段线或多边形。", + "mesh based": "基于网格", + "precision1": "精度 1", + "precision2": "精度 2", + "bitbybit.occt.booleans.meshMeshIntersectionPoints": "网格与网格交点", + "bitbybit.occt.booleans.meshMeshIntersectionPoints_description": "在两个形状之间执行网格与网格求交操作 - 两个形状都可以有自己的网格划分精度。此算法对网格进行求交,并返回交点。", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires": "网格与网格相交转线框", + "meshMeshIntersectionOfShapesWires": "网格与网格相交转线框", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesWires_description": "在一个形状与多个其他形状之间执行网格与网格求交操作 - 所有形状都可以有自己的网格划分精度。此算法对网格进行求交,并返回相交部分的线框,这些线框是多段线或多边形。", + "precisionShapes": "形状精度", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints": "网格与网格相交转点", + "meshMeshIntersectionOfShapesPoints": "网格与网格相交转点", + "bitbybit.occt.booleans.meshMeshIntersectionOfShapesPoints_description": "在一个形状与多个其他形状之间执行网格与网格求交操作 - 所有形状都可以有自己的网格划分精度。此算法对网格进行求交,并返回交点。", + "bitbybit.occt.shapes.face.hexagonsInGrid": "网格中的六边形", + "bitbybit.occt.shapes.face.hexagonsInGrid_description": "在网格中创建 OpenCascade 六边形面", + "bitbybit.jscad.toPolygonPoints": "转为多边形点", + "toPolygonPoints": "转为多边形点", + "bitbybit.jscad.toPolygonPoints_description": "将 jscad 形状转换为表示网格的多边形点集合", + "conversions": "转换", + "bitbybit.manifold.toPolygonPoints": "转为多边形点", + "bitbybit.manifold.toPolygonPoints_description": "将 manifold 形状转换为表示网格的多边形点集合。", + "flatU": "平坦 U", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles": "细分为六边形孔洞", + "subdivideToHexagonHoles": "细分为六边形孔洞", + "bitbybit.occt.shapes.face.subdivideToHexagonHoles_description": "将面细分为六边形孔洞", + "bitbybit.verb.curve.convertLinesToNurbsCurves": "将直线转换为 NURBS 曲线", + "bitbybit.verb.curve.convertLinesToNurbsCurves_description": "将直线转换为 NURBS 曲线。返回 verbnurbs Line 对象的数组。", + "bitbybit.verb.curve.convertLineToNurbsCurve": "将直线转换为 NURBS 曲线", + "convertLineToNurbsCurve": "将直线转换为 NURBS 曲线", + "bitbybit.verb.curve.convertLineToNurbsCurve_description": "将直线转换为 NURBS 曲线。返回 verbnurbs Line 对象。", + "Base.Line3": "Base.Line3", + "bitbybit.verb.curve.convertPolylineToNurbsCurve": "将多段线转换为 NURBS 曲线", + "convertPolylineToNurbsCurve": "将多段线转换为 NURBS 曲线", + "bitbybit.verb.curve.convertPolylineToNurbsCurve_description": "将多段线转换为 NURBS 曲线。返回 verbnurbs NurbsCurve 对象。", + "Base.Polyline3": "Base.Polyline3", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves": "将多段线转换为 NURBS 曲线", + "convertPolylinesToNurbsCurves": "将多段线转换为 NURBS 曲线", + "bitbybit.verb.curve.convertPolylinesToNurbsCurves_description": "将多段线转换为 NURBS 曲线。返回 verbnurbs NurbsCurve 对象。", + "Base.Polyline3[]": "Base.Polyline3[]" } \ No newline at end of file diff --git a/package.json b/package.json index c02ecab3..d89340f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitbybit", - "version": "0.20.2", + "version": "0.20.3", "description": "Monorepo for browser CAD which holds bitbybit.dev npm packages", "main": "index.js", "scripts": { diff --git a/packages/dev/babylonjs/lib/api/bitbybit-base.ts b/packages/dev/babylonjs/lib/api/bitbybit-base.ts index f8e8046a..0ce4158f 100644 --- a/packages/dev/babylonjs/lib/api/bitbybit-base.ts +++ b/packages/dev/babylonjs/lib/api/bitbybit-base.ts @@ -86,11 +86,12 @@ export class BitByBitBase { this.tag, this.context); + this.lists = new Lists(); this.color = new Color(this.math); this.transforms = new Transforms(this.vector, this.math); - this.point = new Point(geometryHelper, this.transforms, this.vector); - this.line = new Line(this.point, geometryHelper); - this.polyline = new Polyline(this.vector, this.point, geometryHelper); + this.point = new Point(geometryHelper, this.transforms, this.vector, this.lists); + this.line = new Line(this.vector, this.point, geometryHelper); + this.polyline = new Polyline(this.vector, this.point, this.line, geometryHelper); this.verb = new Verb(this.context, geometryHelper, this.math); this.time = new Time(this.context); this.occt = new OCCTW(this.context, this.occtWorkerManager); @@ -99,7 +100,6 @@ export class BitByBitBase { this.json = new JSONBitByBit(this.context); this.text = new TextBitByBit(this.point); this.dates = new Dates(); - this.lists = new Lists(); this.mesh = new MeshBitByBit(this.vector, this.polyline); } diff --git a/packages/dev/babylonjs/lib/api/bitbybit/babylon/scene.ts b/packages/dev/babylonjs/lib/api/bitbybit/babylon/scene.ts index 2c642430..cc12e898 100644 --- a/packages/dev/babylonjs/lib/api/bitbybit/babylon/scene.ts +++ b/packages/dev/babylonjs/lib/api/bitbybit/babylon/scene.ts @@ -335,14 +335,14 @@ export class BabylonScene { enableSkybox(inputs: Inputs.BabylonScene.SkyboxDto): void { let texture: BABYLON.CubeTexture; if (inputs.skybox === Inputs.Base.skyboxEnum.default) { - texture = new BABYLON.CubeTexture("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.2/textures/skybox/default_skybox/skybox", this.context.scene); + texture = new BABYLON.CubeTexture("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.3/textures/skybox/default_skybox/skybox", this.context.scene); } else if (inputs.skybox === Inputs.Base.skyboxEnum.greyGradient) { - texture = new BABYLON.CubeTexture("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.2/textures/skybox/grey_gradient/skybox", this.context.scene); + texture = new BABYLON.CubeTexture("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.3/textures/skybox/grey_gradient/skybox", this.context.scene); } else if (inputs.skybox === Inputs.Base.skyboxEnum.clearSky) { - texture = BABYLON.CubeTexture.CreateFromPrefilteredData("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.2/textures/skybox/clear_sky/environment.env", + texture = BABYLON.CubeTexture.CreateFromPrefilteredData("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.3/textures/skybox/clear_sky/environment.env", this.context.scene, false, false); } else if (inputs.skybox === Inputs.Base.skyboxEnum.city) { - texture = BABYLON.CubeTexture.CreateFromPrefilteredData("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.2/textures/skybox/city/environmentSpecular.env", + texture = BABYLON.CubeTexture.CreateFromPrefilteredData("https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.3/textures/skybox/city/environmentSpecular.env", this.context.scene, false, false); } diff --git a/packages/dev/babylonjs/package-lock.json b/packages/dev/babylonjs/package-lock.json index 1b87a4c8..56d6fb64 100644 --- a/packages/dev/babylonjs/package-lock.json +++ b/packages/dev/babylonjs/package-lock.json @@ -1,21 +1,21 @@ { "name": "@bitbybit-dev/babylonjs", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/babylonjs", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@babylonjs/core": "8.4.0", - "@babylonjs/gui": "8.4.0", + "@babylonjs/core": "8.6.1", + "@babylonjs/gui": "8.6.1", "@babylonjs/havok": "1.3.10", - "@babylonjs/loaders": "8.4.0", - "@babylonjs/materials": "8.4.0", - "@babylonjs/serializers": "8.4.0", - "@bitbybit-dev/core": "0.20.2", + "@babylonjs/loaders": "8.6.1", + "@babylonjs/materials": "8.6.1", + "@babylonjs/serializers": "8.6.1", + "@bitbybit-dev/core": "0.20.3", "earcut": "2.2.3" }, "devDependencies": { @@ -1702,14 +1702,14 @@ } }, "node_modules/@babylonjs/core": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.4.0.tgz", - "integrity": "sha512-N/uuM0uGu1GLororCVBn68FSGoiCn96xfDKhwfMlwaxYBF8k2905so092yQUQrscFAiM5EAp76gZpqNtQg9AFg==" + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.6.1.tgz", + "integrity": "sha512-7/VhqaDPdtav0vGoGJ7XwGGzhcoeY4M3/C4ROsqHdD8lpsFvG94CDzmIRON+FGSB3mOCB59Vs84SPSpW18Unlg==" }, "node_modules/@babylonjs/gui": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.4.0.tgz", - "integrity": "sha512-tFG/rpN2Wf9Y7/EXEOqz1EBtFOVkWI9Ui78ezDy0T0fcXFQ8ALp/lfcDpG4+hx2Qa1fOEZPlfCEUxMYWt+Q2Gw==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.6.1.tgz", + "integrity": "sha512-LuOY5if9jajfSdfjmeaRCFzYQIto6q7AeS6CH/HnOuY7ZAPTx2ITrFGB1sB3DevvLdpPbG4zmpwR5Zf3d1rGhQ==", "peerDependencies": { "@babylonjs/core": "^8.0.0" } @@ -1723,26 +1723,26 @@ } }, "node_modules/@babylonjs/loaders": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.4.0.tgz", - "integrity": "sha512-YeOUuFdjpXqBb0X2UcPXIChkHyppkywKKCanh9WAy6Xpk1f9OEMvg9xHLpMs3P2cPI+xYhHtqoht6LPHj6EIIg==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.6.1.tgz", + "integrity": "sha512-Kv5gYJz5KNLF3txBFDrrxT77qOeHF2msKBI6dVAgRFIuJ60CaC881gWOZl3TkgMDDlUrtyzGMTd8A+coBBAIDg==", "peerDependencies": { "@babylonjs/core": "^8.0.0", "babylonjs-gltf2interface": "^8.0.0" } }, "node_modules/@babylonjs/materials": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.4.0.tgz", - "integrity": "sha512-b8Rj2TQ6UABu5SmEOz2im+MfIonc877LG2sOrqwobE6765a1kwp54P9VsQ5PJflUeP7mZCP7EBbaPivP1t+DkQ==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.6.1.tgz", + "integrity": "sha512-uq/agEd0ArINlCX3JB5CNAb77AZT5DYuoR4e3QM8OtxcmZKNTNGJl7co4Hf6hWScNwqoq4EGtvMmgbt/ul95OQ==", "peerDependencies": { "@babylonjs/core": "^8.0.0" } }, "node_modules/@babylonjs/serializers": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.4.0.tgz", - "integrity": "sha512-ZOdwicpdv6lLPUZQOlP1UW54lSAEPazW831FoBupBTfYleXm5/YVQvHWC4IsuJ0qKjqGUg+L8jWc9tNxJSBQLQ==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.6.1.tgz", + "integrity": "sha512-1BbSBPRTUcwaPs0c83pTzirpW/tlh5wkjF8rVUbF4f3i3mfZlfntGkvKGbisfSJXUrILvsIFcbSVP4yTUv5rHw==", "peerDependencies": { "@babylonjs/core": "^8.0.0", "babylonjs-gltf2interface": "^8.0.0" @@ -1755,30 +1755,30 @@ "dev": true }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.2.tgz", - "integrity": "sha512-IYa7ThvCgOMS9CoRvuwTJ/YhAyCVZ0gamXmtb9dYF33RuhtRDWc6nxgitXQ/WBUQI/Csk7hzXlJS32nChMqCNA==", - "dependencies": { - "@bitbybit-dev/base": "0.20.2", - "@bitbybit-dev/jscad-worker": "0.20.2", - "@bitbybit-dev/manifold-worker": "0.20.2", - "@bitbybit-dev/occt-worker": "0.20.2", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.3.tgz", + "integrity": "sha512-SVSHyvysBJGK80YBUf/W0AAqc2561svpE6xFORnyq7i8PrBjWnKPgWHRn138lUloff4aS40s6QFCqerN3IFM7Q==", + "dependencies": { + "@bitbybit-dev/base": "0.20.3", + "@bitbybit-dev/jscad-worker": "0.20.3", + "@bitbybit-dev/manifold-worker": "0.20.3", + "@bitbybit-dev/occt-worker": "0.20.3", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "dependencies": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -1787,45 +1787,45 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.2.tgz", - "integrity": "sha512-bTnlO3yqvTNFqAx3dGnhrX/O7HPNuXRE5vn68ogK7belWEI8r26XaN5kJEzcN8k9maxAXJQRR04xJQPkbfGTaA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.3.tgz", + "integrity": "sha512-V1B7FPvpyDbDjmlunaaCSUo2kkMT164YaDoQMRNGjxknLI+lzymz13W7J99VZrlekJY2RSaMDaYx+9k0ELXbtw==", "dependencies": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "dependencies": { "manifold-3d": "3.0.0" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.2.tgz", - "integrity": "sha512-+Zid8ALBXGTSLn6DlhU8abHduu5HlUpFZ5+RoIshXi4z5xQJbVhEWckDn1PF2Uq9y4qnwHTMQK+etF40Aei2MQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.3.tgz", + "integrity": "sha512-EWN8k6yXK1XWXK/Km4Jx3xB9H3joEi1XJo48OU8CQHRMvplpMt0P5LmqiskyT9Y5Gm16YLOVm/8AX6f4Vra9vQ==", "dependencies": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "dependencies": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.2.tgz", - "integrity": "sha512-q6h/PbBAKnp01PshmhGE14x2b7YnW02YvnUrdGkU6FVYErxoQwwASiN10UKgeBbkD+jbc099kpFzJtvamPnMRw==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.3.tgz", + "integrity": "sha512-2fDVkW42TPzrAswTCX9jWU96L2TkTGssIb82iX+ORnv8vpUb/P7Ih5vMsUUkHLZmFyft61dTvPDSjN1bqPAgQw==", "dependencies": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" } }, @@ -8451,14 +8451,14 @@ } }, "@babylonjs/core": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.4.0.tgz", - "integrity": "sha512-N/uuM0uGu1GLororCVBn68FSGoiCn96xfDKhwfMlwaxYBF8k2905so092yQUQrscFAiM5EAp76gZpqNtQg9AFg==" + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.6.1.tgz", + "integrity": "sha512-7/VhqaDPdtav0vGoGJ7XwGGzhcoeY4M3/C4ROsqHdD8lpsFvG94CDzmIRON+FGSB3mOCB59Vs84SPSpW18Unlg==" }, "@babylonjs/gui": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.4.0.tgz", - "integrity": "sha512-tFG/rpN2Wf9Y7/EXEOqz1EBtFOVkWI9Ui78ezDy0T0fcXFQ8ALp/lfcDpG4+hx2Qa1fOEZPlfCEUxMYWt+Q2Gw==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-8.6.1.tgz", + "integrity": "sha512-LuOY5if9jajfSdfjmeaRCFzYQIto6q7AeS6CH/HnOuY7ZAPTx2ITrFGB1sB3DevvLdpPbG4zmpwR5Zf3d1rGhQ==", "requires": {} }, "@babylonjs/havok": { @@ -8470,21 +8470,21 @@ } }, "@babylonjs/loaders": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.4.0.tgz", - "integrity": "sha512-YeOUuFdjpXqBb0X2UcPXIChkHyppkywKKCanh9WAy6Xpk1f9OEMvg9xHLpMs3P2cPI+xYhHtqoht6LPHj6EIIg==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-8.6.1.tgz", + "integrity": "sha512-Kv5gYJz5KNLF3txBFDrrxT77qOeHF2msKBI6dVAgRFIuJ60CaC881gWOZl3TkgMDDlUrtyzGMTd8A+coBBAIDg==", "requires": {} }, "@babylonjs/materials": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.4.0.tgz", - "integrity": "sha512-b8Rj2TQ6UABu5SmEOz2im+MfIonc877LG2sOrqwobE6765a1kwp54P9VsQ5PJflUeP7mZCP7EBbaPivP1t+DkQ==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-8.6.1.tgz", + "integrity": "sha512-uq/agEd0ArINlCX3JB5CNAb77AZT5DYuoR4e3QM8OtxcmZKNTNGJl7co4Hf6hWScNwqoq4EGtvMmgbt/ul95OQ==", "requires": {} }, "@babylonjs/serializers": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.4.0.tgz", - "integrity": "sha512-ZOdwicpdv6lLPUZQOlP1UW54lSAEPazW831FoBupBTfYleXm5/YVQvHWC4IsuJ0qKjqGUg+L8jWc9tNxJSBQLQ==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-8.6.1.tgz", + "integrity": "sha512-1BbSBPRTUcwaPs0c83pTzirpW/tlh5wkjF8rVUbF4f3i3mfZlfntGkvKGbisfSJXUrILvsIFcbSVP4yTUv5rHw==", "requires": {} }, "@bcoe/v8-coverage": { @@ -8494,30 +8494,30 @@ "dev": true }, "@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "@bitbybit-dev/core": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.2.tgz", - "integrity": "sha512-IYa7ThvCgOMS9CoRvuwTJ/YhAyCVZ0gamXmtb9dYF33RuhtRDWc6nxgitXQ/WBUQI/Csk7hzXlJS32nChMqCNA==", - "requires": { - "@bitbybit-dev/base": "0.20.2", - "@bitbybit-dev/jscad-worker": "0.20.2", - "@bitbybit-dev/manifold-worker": "0.20.2", - "@bitbybit-dev/occt-worker": "0.20.2", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.3.tgz", + "integrity": "sha512-SVSHyvysBJGK80YBUf/W0AAqc2561svpE6xFORnyq7i8PrBjWnKPgWHRn138lUloff4aS40s6QFCqerN3IFM7Q==", + "requires": { + "@bitbybit-dev/base": "0.20.3", + "@bitbybit-dev/jscad-worker": "0.20.3", + "@bitbybit-dev/manifold-worker": "0.20.3", + "@bitbybit-dev/occt-worker": "0.20.3", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "requires": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -8526,45 +8526,45 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.2.tgz", - "integrity": "sha512-bTnlO3yqvTNFqAx3dGnhrX/O7HPNuXRE5vn68ogK7belWEI8r26XaN5kJEzcN8k9maxAXJQRR04xJQPkbfGTaA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.3.tgz", + "integrity": "sha512-V1B7FPvpyDbDjmlunaaCSUo2kkMT164YaDoQMRNGjxknLI+lzymz13W7J99VZrlekJY2RSaMDaYx+9k0ELXbtw==", "requires": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "requires": { "manifold-3d": "3.0.0" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.2.tgz", - "integrity": "sha512-+Zid8ALBXGTSLn6DlhU8abHduu5HlUpFZ5+RoIshXi4z5xQJbVhEWckDn1PF2Uq9y4qnwHTMQK+etF40Aei2MQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.3.tgz", + "integrity": "sha512-EWN8k6yXK1XWXK/Km4Jx3xB9H3joEi1XJo48OU8CQHRMvplpMt0P5LmqiskyT9Y5Gm16YLOVm/8AX6f4Vra9vQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "requires": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.2.tgz", - "integrity": "sha512-q6h/PbBAKnp01PshmhGE14x2b7YnW02YvnUrdGkU6FVYErxoQwwASiN10UKgeBbkD+jbc099kpFzJtvamPnMRw==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.3.tgz", + "integrity": "sha512-2fDVkW42TPzrAswTCX9jWU96L2TkTGssIb82iX+ORnv8vpUb/P7Ih5vMsUUkHLZmFyft61dTvPDSjN1bqPAgQw==", "requires": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" } }, diff --git a/packages/dev/babylonjs/package.json b/packages/dev/babylonjs/package.json index 772a05dc..ea72409d 100644 --- a/packages/dev/babylonjs/package.json +++ b/packages/dev/babylonjs/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/babylonjs", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers BABYLONJS CAD Library to Program Geometry", "main": "index.js", "repository": { @@ -54,13 +54,13 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "@babylonjs/core": "8.4.0", - "@babylonjs/gui": "8.4.0", - "@babylonjs/loaders": "8.4.0", - "@babylonjs/materials": "8.4.0", - "@babylonjs/serializers": "8.4.0", + "@babylonjs/core": "8.6.1", + "@babylonjs/gui": "8.6.1", + "@babylonjs/loaders": "8.6.1", + "@babylonjs/materials": "8.6.1", + "@babylonjs/serializers": "8.6.1", "@babylonjs/havok": "1.3.10", - "@bitbybit-dev/core": "0.20.2", + "@bitbybit-dev/core": "0.20.3", "earcut": "2.2.3" }, "devDependencies": { diff --git a/packages/dev/base/lib/api/inputs/line-inputs.ts b/packages/dev/base/lib/api/inputs/line-inputs.ts index f82a0a2f..852b4cfe 100644 --- a/packages/dev/base/lib/api/inputs/line-inputs.ts +++ b/packages/dev/base/lib/api/inputs/line-inputs.ts @@ -190,6 +190,36 @@ export namespace Line { */ lines?: LinePointsDto[]; } + export class LineLineIntersectionDto { + constructor(line1?: LinePointsDto, line2?: LinePointsDto, tolerance?: number) { + if (line1 !== undefined) { this.line1 = line1; } + if (line2 !== undefined) { this.line2 = line2; } + if (tolerance !== undefined) { this.tolerance = tolerance; } + } + /** + * First line + * @default undefined + */ + line1?: LinePointsDto; + /** + * Second line + * @default undefined + */ + line2?: LinePointsDto; + /** + * Set to false if you want to check for infinite lines + * @default true + */ + checkSegmentsOnly? = true; + /** + * Tolerance for intersection + * @default 0.01 + * @minimum 0 + * @maximum Infinity + * @step 0.1 + */ + tolerance?: number; + } export class PointOnLineDto { constructor(line?: LinePointsDto, param?: number) { if (line !== undefined) { this.line = line; } diff --git a/packages/dev/base/lib/api/inputs/point-inputs.ts b/packages/dev/base/lib/api/inputs/point-inputs.ts index 3d934e5d..38cd53ed 100644 --- a/packages/dev/base/lib/api/inputs/point-inputs.ts +++ b/packages/dev/base/lib/api/inputs/point-inputs.ts @@ -304,6 +304,38 @@ export namespace Point { */ scaleXyz: Base.Vector3 = [1, 1, 1]; } + + export class StretchPointsDirFromCenterDto { + constructor(points?: Base.Point3[], center?: Base.Point3, direction?: Base.Vector3, scale?: number) { + if (points !== undefined) { this.points = points; } + if (center !== undefined) { this.center = center; } + if (direction !== undefined) { this.direction = direction; } + if (scale !== undefined) { this.scale = scale; } + } + /** + * Points to transform + * @default undefined + */ + points?: Base.Point3[]; + /** + * The center from which the scaling is applied + * @default [0, 0, 0] + */ + center?: Base.Point3 = [0, 0, 0]; + /** + * Stretch direction vector + * @default [0, 0, 1] + */ + direction?: Base.Vector3 = [0, 0, 1]; + /** + * The scale factor to apply along the direction vector. 1.0 means no change. + * @default 2 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + scale? = 2; + } export class RotatePointsCenterAxisDto { constructor(points?: Base.Point3[], angle?: number, axis?: Base.Vector3, center?: Base.Point3) { if (points !== undefined) { this.points = points; } @@ -379,6 +411,62 @@ export namespace Point { */ reverseNormal = false; } + export class ThreePointsToleranceDto { + constructor(start?: Base.Point3, center?: Base.Point3, end?: Base.Point3, tolerance?: number) { + if (start !== undefined) { this.start = start; } + if (center !== undefined) { this.center = center; } + if (end !== undefined) { this.end = end; } + if (tolerance !== undefined) { this.tolerance = tolerance; } + } + /** + * Start point + * @default undefined + */ + start?: Base.Point3; + /** + * Center point + * @default undefined + */ + center?: Base.Point3; + /** + * End point + * @default undefined + */ + end?: Base.Point3; + /** + * Tolerance for the calculation + * @default 1e-7 + * @minimum -Infinity + * @maximum Infinity + * @step 1e-7 + */ + tolerance = 1e-7; + } + export class PointsMaxFilletsHalfLineDto { + constructor(points?: Base.Point3[], checkLastWithFirst?: boolean, tolerance?: number) { + if (points !== undefined) { this.points = points; } + if (checkLastWithFirst !== undefined) { this.checkLastWithFirst = checkLastWithFirst; } + if (tolerance !== undefined) { this.tolerance = tolerance; } + } + /** + * Points to transform + * @default undefined + */ + points?: Base.Point3[]; + /** + * Check first and last point for duplicates + * @default false + */ + checkLastWithFirst? = false; + /** + * Tolerance for the calculation + * @default 1e-7 + * @minimum -Infinity + * @maximum Infinity + * @step 1e-7 + */ + tolerance? = 1e-7; + } export class RemoveConsecutiveDuplicatesDto { constructor(points?: Base.Point3[], tolerance?: number, checkFirstAndLast?: boolean) { if (points !== undefined) { this.points = points; } @@ -543,7 +631,72 @@ export namespace Point { */ factor = 1; } - + export class HexGridScaledToFitDto { + constructor(wdith?: number, height?: number, nrHexagonsU?: number, nrHexagonsV?: number, centerGrid?: boolean, pointsOnGround?: boolean) { + if (wdith !== undefined) { this.width = wdith; } + if (height !== undefined) { this.height = height; } + if (nrHexagonsU !== undefined) { this.nrHexagonsInHeight = nrHexagonsU; } + if (nrHexagonsV !== undefined) { this.nrHexagonsInWidth = nrHexagonsV; } + if (centerGrid !== undefined) { this.centerGrid = centerGrid; } + if (pointsOnGround !== undefined) { this.pointsOnGround = pointsOnGround; } + } + /** Total desired width for the grid area. The hexagon size will be derived from this and nrHexagonsU. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 0.1 + */ + width? = 10; + /** Total desired height for the grid area. Note: due to hexagon geometry, the actual grid height might differ slightly if maintaining regular hexagons based on width. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 0.1 + */ + height? = 10; + /** Number of hexagons desired in width. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + nrHexagonsInWidth? = 10; + /** Number of hexagons desired in height. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + nrHexagonsInHeight? = 10; + /** If true, the hexagons will be oriented with their flat sides facing up and down. + * @default false + */ + flatTop? = false; + /** If true, shift the entire grid up by half hex height. + * @default false + */ + extendTop? = false; + /** If true, shift the entire grid down by half hex height. + * @default false + */ + extendBottom? = false; + /** If true, shift the entire grid left by half hex width. + * @default false + */ + extendLeft? = false; + /** If true, shift the entire grid right by half hex width. + * @default false + */ + extendRight? = false; + /** If true, the grid center (based on totalWidth/totalHeight) will be at [0,0,0]. + * @default false + */ + centerGrid? = false; + /** If true, swaps Y and Z coordinates and sets Y to 0, placing points on the XZ ground plane. + * @default false + */ + pointsOnGround? = false; + } export class HexGridCentersDto { constructor(nrHexagonsX?: number, nrHexagonsY?: number, radiusHexagon?: number, orientOnCenter?: boolean, pointsOnGround?: boolean) { if (nrHexagonsX !== undefined) { this.nrHexagonsX = nrHexagonsX; } diff --git a/packages/dev/base/lib/api/inputs/polyline-inputs.ts b/packages/dev/base/lib/api/inputs/polyline-inputs.ts index c4c58221..821982fe 100644 --- a/packages/dev/base/lib/api/inputs/polyline-inputs.ts +++ b/packages/dev/base/lib/api/inputs/polyline-inputs.ts @@ -197,4 +197,48 @@ export namespace Polyline { */ tolerance? = 1e-5; } + export class PolylineToleranceDto { + constructor(polyline?: PolylinePropertiesDto, tolerance?: number) { + if (polyline !== undefined) { this.polyline = polyline; } + if (tolerance !== undefined) { this.tolerance = tolerance; } + } + /** + * Polyline to check + * @default undefined + */ + polyline?: PolylinePropertiesDto; + /** + * Tolerance for the calculation + * @default 1e-5 + * @minimum -Infinity + * @maximum Infinity + * @step 1e-5 + */ + tolerance? = 1e-5; + } + export class TwoPolylinesToleranceDto { + constructor(polyline1?: PolylinePropertiesDto, polyline2?: PolylinePropertiesDto, tolerance?: number) { + if (polyline1 !== undefined) { this.polyline1 = polyline1; } + if (polyline2 !== undefined) { this.polyline2 = polyline2; } + if (tolerance !== undefined) { this.tolerance = tolerance; } + } + /** + * First polyline to check + * @default undefined + */ + polyline1?: PolylinePropertiesDto; + /** + * Second polyline to check + * @default undefined + */ + polyline2?: PolylinePropertiesDto; + /** + * Tolerance for the calculation + * @default 1e-5 + * @minimum -Infinity + * @maximum Infinity + * @step 1e-5 + */ + tolerance? = 1e-5; + } } diff --git a/packages/dev/base/lib/api/inputs/transforms-inputs.ts b/packages/dev/base/lib/api/inputs/transforms-inputs.ts index 4449b715..adad63c4 100644 --- a/packages/dev/base/lib/api/inputs/transforms-inputs.ts +++ b/packages/dev/base/lib/api/inputs/transforms-inputs.ts @@ -94,6 +94,28 @@ export namespace Transforms { */ scaleXyz: Base.Vector3 = [1, 1, 1]; } + export class StretchDirCenterDto { + constructor(scale?: number, center?: Base.Point3, direction?: Base.Vector3) { + if (scale !== undefined) { this.scale = scale; } + if (center !== undefined) { this.center = center; } + if (direction !== undefined) { this.direction = direction; } + } + /** The center point around which to stretch. + * @default [0, 0, 0] + */ + center?: Base.Point3 = [0, 0, 0]; + /** The direction vector along which to stretch. Does not need to be normalized initially. + * @default [0, 0, 1] + */ + direction?: Base.Vector3 = [0, 0, 1]; + /** The scale factor to apply along the direction vector. 1.0 means no change. + * @default 2 + * @minimum -Infinity + * @maximum Infinity + * @step 0.1 + */ + scale? = 2; + } export class ScaleCenterXYZDto { constructor(center?: Base.Point3, scaleXyz?: Base.Vector3) { if (center !== undefined) { this.center = center; } diff --git a/packages/dev/base/lib/api/models/index.ts b/packages/dev/base/lib/api/models/index.ts index e20cd3f8..ff48665c 100644 --- a/packages/dev/base/lib/api/models/index.ts +++ b/packages/dev/base/lib/api/models/index.ts @@ -1 +1,2 @@ export * from "./text"; +export * from "./point"; \ No newline at end of file diff --git a/packages/dev/base/lib/api/models/point/bucket.ts b/packages/dev/base/lib/api/models/point/bucket.ts new file mode 100644 index 00000000..8ae6cd5a --- /dev/null +++ b/packages/dev/base/lib/api/models/point/bucket.ts @@ -0,0 +1 @@ +export * from "./hex-grid-data"; diff --git a/packages/dev/base/lib/api/models/point/hex-grid-data.ts b/packages/dev/base/lib/api/models/point/hex-grid-data.ts new file mode 100644 index 00000000..19bc405c --- /dev/null +++ b/packages/dev/base/lib/api/models/point/hex-grid-data.ts @@ -0,0 +1,9 @@ +import { Base } from "../../inputs/base-inputs"; + +export class HexGridData { + centers: Base.Point3[]; + hexagons: Base.Point3[][]; + shortestDistEdge: number; + longestDistEdge: number; + maxFilletRadius: number; +} \ No newline at end of file diff --git a/packages/dev/base/lib/api/models/point/index.ts b/packages/dev/base/lib/api/models/point/index.ts new file mode 100644 index 00000000..0f0cf905 --- /dev/null +++ b/packages/dev/base/lib/api/models/point/index.ts @@ -0,0 +1 @@ +export * as Point from "./bucket"; diff --git a/packages/dev/base/lib/api/services/line.test.ts b/packages/dev/base/lib/api/services/line.test.ts index 7ac7fcf6..95416a05 100644 --- a/packages/dev/base/lib/api/services/line.test.ts +++ b/packages/dev/base/lib/api/services/line.test.ts @@ -2,66 +2,37 @@ import { GeometryHelper } from "./geometry-helper"; import { MathBitByBit } from "./math"; import { Point } from "./point"; import { Line } from "./line"; +import { Lists } from "./lists"; import { Transforms } from "./transforms"; import { Vector } from "./vector"; import * as Inputs from "../inputs"; +import { UnitTestHelper } from "../unit-test-helper"; describe("Line unit tests", () => { + const uh = new UnitTestHelper(); + let geometryHelper: GeometryHelper; let math: MathBitByBit; let vector: Vector; let point: Point; let line: Line; + let lists: Lists; let transforms: Transforms; // Precision for floating point comparisons const TOLERANCE = 1e-7; - // Helper to compare points/vectors with tolerance - const expectPointCloseTo = ( - received: Inputs.Base.Point3 | Inputs.Base.Vector3 | undefined, - expected: Inputs.Base.Point3 | Inputs.Base.Vector3 - ) => { - expect(received).toBeDefined(); - if (!received) return; // Guard for TS - expect(received.length).toEqual(expected.length); - expect(received[0]).toBeCloseTo(expected[0], TOLERANCE); - expect(received[1]).toBeCloseTo(expected[1], TOLERANCE); - if (expected.length > 2 && received.length > 2) { - expect(received[2]).toBeCloseTo(expected[2], TOLERANCE); - } - }; - - // Helper to compare lines with tolerance - const expectLineCloseTo = ( - received: Inputs.Base.Line3 | undefined, - expected: Inputs.Base.Line3 - ) => { - expect(received).toBeDefined(); - if (!received) return; - expectPointCloseTo(received.start, expected.start); - expectPointCloseTo(received.end, expected.end); - }; - - // Helper to compare arrays of lines with tolerance - const expectLinesCloseTo = ( - received: Inputs.Base.Line3[], - expected: Inputs.Base.Line3[] - ) => { - expect(received.length).toEqual(expected.length); - received.forEach((l, i) => expectLineCloseTo(l, expected[i])); - }; - beforeAll(() => { geometryHelper = new GeometryHelper(); math = new MathBitByBit(); vector = new Vector(math, geometryHelper); transforms = new Transforms(vector, math); - point = new Point(geometryHelper, transforms, vector); - line = new Line(point, geometryHelper); + lists = new Lists(); + point = new Point(geometryHelper, transforms, vector, lists); + line = new Line(vector, point, geometryHelper); }); @@ -115,7 +86,7 @@ describe("Line unit tests", () => { const transformation = transforms.translationXYZ({ translation: [10, 5, -2] }); const result = line.transformLine({ line: inputLine, transformation }); const expectedLine: Inputs.Base.Line3 = { start: [10, 5, -2], end: [11, 5, -2] }; - expectLineCloseTo(result, expectedLine); + uh.expectLineCloseTo(result, expectedLine); }); it("should rotate the line", () => { @@ -123,7 +94,7 @@ describe("Line unit tests", () => { const transformation = transforms.rotationCenterAxis({ center: [0, 0, 0], axis: [0, 0, 1], angle: 90 }); const result = line.transformLine({ line: inputLine, transformation }); const expectedLine: Inputs.Base.Line3 = { start: [0, 1, 0], end: [0, 2, 5] }; - expectLineCloseTo(result, expectedLine); + uh.expectLineCloseTo(result, expectedLine); }); it("should return a new object", () => { @@ -151,7 +122,7 @@ describe("Line unit tests", () => { { start: [0, 10, 0], end: [1, 10, 0] }, { start: [5, 5, 5], end: [5, 5, 6] } // Rotation around X maps Y=1 -> Z=1 ]; - expectLinesCloseTo(result, expectedLines); + uh.expectLinesCloseTo(result, expectedLines); }); it("should handle empty input arrays", () => { @@ -179,32 +150,32 @@ describe("Line unit tests", () => { it("should return the start point when param is 0", () => { const result = line.getPointOnLine({ line: testLine, param: 0 }); - expectPointCloseTo(result, testLine.start); + uh.expectPointCloseTo(result, testLine.start); }); it("should return the end point when param is 1", () => { const result = line.getPointOnLine({ line: testLine, param: 1 }); - expectPointCloseTo(result, testLine.end); + uh.expectPointCloseTo(result, testLine.end); }); it("should return the midpoint when param is 0.5", () => { const result = line.getPointOnLine({ line: testLine, param: 0.5 }); const expectedMidpoint: Inputs.Base.Point3 = [5, 10, -15]; - expectPointCloseTo(result, expectedMidpoint); + uh.expectPointCloseTo(result, expectedMidpoint); }); it("should extrapolate backward when param is < 0", () => { const result = line.getPointOnLine({ line: testLine, param: -0.5 }); // Direction = [10, 20, -30]. Start + (-0.5)*Dir = [0,0,0] + [-5, -10, 15] const expectedPoint: Inputs.Base.Point3 = [-5, -10, 15]; - expectPointCloseTo(result, expectedPoint); + uh.expectPointCloseTo(result, expectedPoint); }); it("should extrapolate forward when param is > 1", () => { const result = line.getPointOnLine({ line: testLine, param: 1.2 }); // Start + (1.2)*Dir = [0,0,0] + [12, 24, -36] const expectedPoint: Inputs.Base.Point3 = [12, 24, -36]; - expectPointCloseTo(result, expectedPoint); + uh.expectPointCloseTo(result, expectedPoint); }); }); @@ -330,5 +301,209 @@ describe("Line unit tests", () => { }); }); + + describe("lineLineIntersection", () => { + + // --- Test Data --- + const ORIGIN: Inputs.Base.Point3 = [0, 0, 0]; + + // Basic intersecting lines (X and Y axes) + const lineX: Inputs.Base.Line3 = { start: [-5, 0, 0], end: [5, 0, 0] }; + const lineY: Inputs.Base.Line3 = { start: [0, -5, 0], end: [0, 5, 0] }; + const expectedXYIntersect: Inputs.Base.Point3 = [0, 0, 0]; + + // Lines intersecting outside segments + const lineXshort: Inputs.Base.Line3 = { start: [1, 0, 0], end: [5, 0, 0] }; + const lineYshort: Inputs.Base.Line3 = { start: [0, 1, 0], end: [0, 5, 0] }; + + // Lines intersecting at endpoint + const lineXoriginEnd: Inputs.Base.Line3 = { start: [-5, 0, 0], end: [0, 0, 0] }; + const lineYoriginStart: Inputs.Base.Line3 = { start: [0, 0, 0], end: [0, 5, 0] }; + + // Skew lines + const lineXoffsetY: Inputs.Base.Line3 = { start: [-5, 1, 0], end: [5, 1, 0] }; + const lineY_offsetZ: Inputs.Base.Line3 = { start: [0, -5, 1], end: [0, 5, 1] }; + const lineSkewDiag: Inputs.Base.Line3 = { start: [-5, -5, 5], end: [5, 5, 10] }; + + // Parallel non-collinear lines + const lineXoffsetZ: Inputs.Base.Line3 = { start: [-5, 0, 1], end: [5, 0, 1] }; + + // Collinear lines + const lineX_0_10: Inputs.Base.Line3 = { start: [0, 0, 0], end: [10, 0, 0] }; + const lineX_5_15: Inputs.Base.Line3 = { start: [5, 0, 0], end: [15, 0, 0] }; + const lineX_10_20: Inputs.Base.Line3 = { start: [10, 0, 0], end: [20, 0, 0] }; + const lineX_11_20: Inputs.Base.Line3 = { start: [11, 0, 0], end: [20, 0, 0] }; + const lineX_neg10_neg5: Inputs.Base.Line3 = { start: [-10, 0, 0], end: [-5, 0, 0] }; + + // Zero length line + const zeroLine: Inputs.Base.Line3 = { start: [1, 1, 1], end: [1, 1, 1] }; + + describe("Intersecting Lines", () => { + it("should find intersection within segments (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineY, checkSegmentsOnly: true }); + uh.expectPointCloseTo(result, expectedXYIntersect); + }); + + it("should find intersection within segments (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineY, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, expectedXYIntersect); + }); + + it("should find intersection at endpoints (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineXoriginEnd, line2: lineYoriginStart, checkSegmentsOnly: true }); + uh.expectPointCloseTo(result, ORIGIN); + }); + + it("should find intersection at endpoints (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineXoriginEnd, line2: lineYoriginStart, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, ORIGIN); + }); + + it("should NOT find intersection when outside segments (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineXshort, line2: lineYshort, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should find intersection when outside segments (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineXshort, line2: lineYshort, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, ORIGIN); // The intersection of infinite lines is origin + }); + + it("should handle near-zero results by clipping them", () => { + const line1: Inputs.Base.Line3 = { start: [-1, 1e-10, 0], end: [1, -1e-10, 0] }; // Crosses Y=0 at X=0 + const line2: Inputs.Base.Line3 = { start: [0, -1, 0], end: [0, 1, 0] }; // Y axis + const expected: Inputs.Base.Point3 = [0, 0, 0]; + const result = line.lineLineIntersection({ line1, line2, checkSegmentsOnly: true, tolerance: 1e-8 }); + uh.expectPointCloseTo(result, expected); + }); + + it("should handle tolerance based intersection", () => { + const line1: Inputs.Base.Line3 = { start: [-5, 0, 0], end: [5, 0, 0] }; + const line2: Inputs.Base.Line3 = { start: [0, -5, 1e-10], end: [0, 5, 1e-10] }; + const expected: Inputs.Base.Point3 = [0, 0, 0]; + // Keep in mind that we use epsilon cubes for tolerance - otherwise this case would be considered skewed + const result = line.lineLineIntersection({ line1, line2, checkSegmentsOnly: true, tolerance: 1e-2 }); + uh.expectPointCloseTo(result, expected); + }); + }); + + describe("Skew Lines", () => { + it("should return undefined for skew lines (offset parallel) (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineY_offsetZ, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should return undefined for skew lines (offset parallel) (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineY_offsetZ, checkSegmentsOnly: false }); + expect(result).toBeUndefined(); + }); + + it("should return undefined for skew lines (diagonal) (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineSkewDiag, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should return undefined for skew lines (diagonal) (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineSkewDiag, checkSegmentsOnly: false }); + expect(result).toBeUndefined(); + }); + + it("should return undefined for another skew case (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineXoffsetY, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should return undefined for another skew case (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineXoffsetY, checkSegmentsOnly: false }); + expect(result).toBeUndefined(); + }); + }); + + describe("Parallel Lines", () => { + it("should return undefined for parallel non-collinear lines (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineXoffsetZ, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should return undefined for parallel non-collinear lines (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: lineXoffsetZ, checkSegmentsOnly: false }); + expect(result).toBeUndefined(); + }); + }); + + describe("Collinear Lines", () => { + + it("should return undefined for overlapping collinear segments (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: lineX_5_15, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); // Intersection is a segment [5,0,0] to [10,0,0] + }); + it("should return undefined for fully contained collinear segments (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: { start: [2, 0, 0], end: [8, 0, 0] }, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + it("should return undefined for touching collinear segments (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: lineX_10_20, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + it("should return undefined for separate collinear segments (checkSegmentsOnly = true)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: lineX_11_20, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should return start point of line1 for overlapping collinear segments (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: lineX_5_15, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, lineX_0_10.start); + }); + + it("should return start point of line1 for fully contained collinear segments (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: { start: [2, 0, 0], end: [8, 0, 0] }, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, lineX_0_10.start); + }); + + it("should return start point of line1 for touching collinear segments (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: lineX_10_20, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, lineX_0_10.start); + }); + + it("should return start point of line1 for separate collinear segments (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: lineX_11_20, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, lineX_0_10.start); + }); + + it("should return start point of line1 for separate collinear segments (negative side) (checkSegmentsOnly = false)", () => { + const result = line.lineLineIntersection({ line1: lineX_0_10, line2: lineX_neg10_neg5, checkSegmentsOnly: false }); + uh.expectPointCloseTo(result, lineX_0_10.start); + }); + }); + + describe("Edge Cases", () => { + + it("should return undefined if line1 has zero length", () => { + const result = line.lineLineIntersection({ line1: zeroLine, line2: lineX, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should return undefined if line2 has zero length", () => { + const result = line.lineLineIntersection({ line1: lineX, line2: zeroLine, checkSegmentsOnly: false }); + expect(result).toBeUndefined(); + }); + + it("should return undefined if both lines have zero length", () => { + const result = line.lineLineIntersection({ line1: zeroLine, line2: zeroLine, checkSegmentsOnly: true }); + expect(result).toBeUndefined(); + }); + + it("should distinguish near-intersecting skew lines based on tolerance", () => { + const line1: Inputs.Base.Line3 = { start: [0, 0, 0], end: [10, 0, 0] }; + const tiny_offset = 1e-5; + const line2_skew: Inputs.Base.Line3 = { start: [5 + tiny_offset, -5, tiny_offset], end: [5 + tiny_offset, 5, tiny_offset] }; + const resultSkewTight = line.lineLineIntersection({ line1, line2: line2_skew, checkSegmentsOnly: true, tolerance: 1e-7 }); + expect(resultSkewTight).toBeUndefined(); + const resultIntersectLoose = line.lineLineIntersection({ line1, line2: line2_skew, checkSegmentsOnly: true, tolerance: 1e-1 }); + expect(resultIntersectLoose).toEqual([5.00001, 0, 0]); + }); + + }); + }); }); diff --git a/packages/dev/base/lib/api/services/line.ts b/packages/dev/base/lib/api/services/line.ts index 96ed1eaf..d0e05967 100644 --- a/packages/dev/base/lib/api/services/line.ts +++ b/packages/dev/base/lib/api/services/line.ts @@ -1,6 +1,7 @@ import { GeometryHelper } from "./geometry-helper"; import * as Inputs from "../inputs"; import { Point } from "./point"; +import { Vector } from "./vector"; /** * Contains various methods for lines and segments. Line in bitbybit is a simple object that has start and end point properties. @@ -8,7 +9,7 @@ import { Point } from "./point"; */ export class Line { - constructor(private readonly point: Point, private readonly geometryHelper: GeometryHelper) { } + constructor(private readonly vector: Vector, private readonly point: Point, private readonly geometryHelper: GeometryHelper) { } /** * Gets the start point of the line @@ -211,6 +212,146 @@ export class Line { segmentsToLines(inputs: Inputs.Line.SegmentsDto): Inputs.Base.Line3[] { return inputs.segments.map(segment => ({ start: segment[0], end: segment[1] })); } + + /** + * If two lines intersect return the intersection point + * @param inputs line1 and line2 + * @returns intersection point or undefined if no intersection + * @group intersection + * @shortname line-line int + * @drawable true + */ + lineLineIntersection(inputs: Inputs.Line.LineLineIntersectionDto): Inputs.Base.Point3 | undefined { + const epsilon = inputs.tolerance || 1e-6; // Default tolerance + const checkSegments = inputs.checkSegmentsOnly; + + const line1 = inputs.line1; + const line2 = inputs.line2; + + // Input validation + if (!line1?.start || !line1.end || !line2?.start || !line2.end || + line1.start.length !== 3 || line1.end.length !== 3 || + line2.start.length !== 3 || line2.end.length !== 3) { + console.error("Invalid line input to lineLineIntersection"); + return undefined; + } + + const p1 = line1.start; + const d1 = this.vector.sub({ first: line1.end, second: line1.start }); // Direction vector line 1 + const p2 = line2.start; + const d2 = this.vector.sub({ first: line2.end, second: line2.start }); // Direction vector line 2 + const p21 = this.vector.sub({ first: p2, second: p1 }); // Vector between start points + + // --- Check for Zero-Length Segments --- + const lenSq1 = this.vector.lengthSq({ vector: d1 as Inputs.Base.Vector3 }); + const lenSq2 = this.vector.lengthSq({ vector: d2 as Inputs.Base.Vector3 }); + + // Compare squared length against squared epsilon + if (lenSq1 < epsilon * epsilon || lenSq2 < epsilon * epsilon) { + return undefined; + } + + // --- Check for Parallelism --- + const d1_cross_d2 = this.vector.cross({ first: d1, second: d2 }); + const crossMagSq = this.vector.lengthSq({ vector: d1_cross_d2 as Inputs.Base.Vector3 }); + + // Check if squared magnitude of cross product is near zero (relative to segment lengths) + // Use epsilon squared as a base tolerance, potentially scale by magnitudes + const parallel_tolerance_sq = epsilon * epsilon; // May need adjustment: * lenSq1 * lenSq2; + if (crossMagSq < parallel_tolerance_sq) { + // Potentially Parallel or Collinear + // Check if collinear: p21 must be parallel to d1 + const p21_cross_d1 = this.vector.cross({ first: p21, second: d1 }); + // Use similar tolerance logic for collinear check + const collinear_tolerance_sq = epsilon * epsilon * lenSq1; // Scale by line1 length + if (this.vector.lengthSq({ vector: p21_cross_d1 as Inputs.Base.Vector3 }) < collinear_tolerance_sq) { + // Collinear + if (!checkSegments) { + return p1; // Infinite lines intersect everywhere, return p1 arbitrarily + } else { + // --- Check for Segment Overlap (Collinear case) --- + const d1d1 = lenSq1; // Reuse calculated squared length + // Avoid division by zero if lenSq1 is extremely small (should be caught earlier) + const safe_d1d1 = (d1d1 < epsilon * epsilon) ? 1.0 : d1d1; + const d1p21 = this.vector.dot({ first: d1, second: p21 }); // Dot product d1·(p2-p1) + const t_p2 = d1p21 / safe_d1d1; // Parameter for p2 projected onto line1's frame + const vec_e2_p1 = this.vector.sub({ first: line2.end, second: p1 }); + const t_e2 = this.vector.dot({ first: d1, second: vec_e2_p1 }) / safe_d1d1; // Param for e2 + + const interval2_t = [Math.min(t_p2, t_e2), Math.max(t_p2, t_e2)]; + const interval1_t = [0, 1]; // Line1 segment parameter range + + const overlap_start = Math.max(interval1_t[0], interval2_t[0]); + const overlap_end = Math.min(interval1_t[1], interval2_t[1]); + // Check for overlap including tolerance + if (overlap_start <= overlap_end + epsilon) { + // Overlap exists, but intersection is a segment, not a single point + return undefined; + } else { + // Collinear but segments do not overlap + return undefined; + } + } + } else { + // Parallel but not collinear + return undefined; + } + } + + // --- Lines are NOT Parallel - Check for Skewness using Scalar Triple Product --- + const scalarTripleProduct = this.vector.dot({ first: p21, second: d1_cross_d2 }); + + // If the scalar triple product is significantly non-zero, the lines are skew. + // Tolerance needs consideration - relates to the "volume" formed by the vectors. + // A simple absolute check against epsilon^3 or similar might work for typical scales. + // Consider scaling tolerance if coordinates can be very large/small. + const skew_tolerance = epsilon * epsilon * epsilon; + if (Math.abs(scalarTripleProduct) > skew_tolerance) { + // Lines are Skew + return undefined; + } + + // --- Lines are Intersecting (Coplanar and Non-Parallel) --- + // Calculate intersection parameters t (for line1) and u (for line2) + // We can use the formulas derived earlier, which are valid for intersecting lines. + const d1d1 = lenSq1; + const d2d2 = lenSq2; + const d1d2 = this.vector.dot({ first: d1, second: d2 }); + const d1p21 = this.vector.dot({ first: d1, second: p21 }); + const d2p21 = this.vector.dot({ first: d2, second: p21 }); + + // Denominator for parameter calculation (same as crossMagSq, essentially) + const denominator = d1d1 * d2d2 - d1d2 * d1d2; + + // Denominator *should* be non-zero based on the parallelism check above, + // but add a defensive check. + if (Math.abs(denominator) < epsilon * epsilon) { + console.error("Internal error: Denominator near zero after non-parallel check."); + return undefined; // Should not happen + } + + const t = (d2d2 * d1p21 - d1d2 * d2p21) / denominator; + const u = (d1d2 * d1p21 - d1d1 * d2p21) / denominator; + + // --- Optional check: Is intersection within segment bounds? --- + if (checkSegments) { + // Check if t and u are within the range [0, 1] (using tolerance) + if (t < -epsilon || t > 1.0 + epsilon || u < -epsilon || u > 1.0 + epsilon) { + return undefined; // Intersection point is outside one or both segments + } + } + + // --- Calculate Intersection Point --- + const intersectionPoint = this.getPointOnLine({ param: t, line: line1 }); + + // Clip near-zero results based on the input epsilon + return [ + Math.abs(intersectionPoint[0]) < epsilon ? 0 : intersectionPoint[0], + Math.abs(intersectionPoint[1]) < epsilon ? 0 : intersectionPoint[1], + Math.abs(intersectionPoint[2]) < epsilon ? 0 : intersectionPoint[2], + ] as Inputs.Base.Point3; + } + } diff --git a/packages/dev/base/lib/api/services/lists.ts b/packages/dev/base/lib/api/services/lists.ts index 283a4d71..7aa193e4 100644 --- a/packages/dev/base/lib/api/services/lists.ts +++ b/packages/dev/base/lib/api/services/lists.ts @@ -261,25 +261,24 @@ export class Lists { * @shortname group elements * @drawable false */ - groupNth(inputs: Inputs.Lists.GroupListDto): T[] { + groupNth(inputs: Inputs.Lists.GroupListDto): T[][] { const groupElements = (inputs: Inputs.Lists.GroupListDto) => { const { nrElements, list, keepRemainder } = inputs; const nrElementsInGroup = nrElements; - const result = []; - let currentGroup = []; + const result: T[][] = []; + let currentGroup: T[] = []; list.forEach((item, index) => { currentGroup.push(item); if ((index + 1) % nrElementsInGroup === 0) { result.push(currentGroup); currentGroup = []; } - if (keepRemainder && index === list.length - 1) { + if (currentGroup.length > 0 && keepRemainder && index === list.length - 1) { result.push(currentGroup); } }); return result; }; - // TODO make this work on any level return groupElements(inputs); } diff --git a/packages/dev/base/lib/api/services/mesh.test.ts b/packages/dev/base/lib/api/services/mesh.test.ts index 428425f6..468eabd3 100644 --- a/packages/dev/base/lib/api/services/mesh.test.ts +++ b/packages/dev/base/lib/api/services/mesh.test.ts @@ -6,76 +6,40 @@ import { Transforms } from "./transforms"; import { Vector } from "./vector"; import * as Inputs from "../inputs"; import { MeshBitByBit } from "./mesh"; +import { TOLERANCE, UnitTestHelper } from "../unit-test-helper"; +import { Line } from "./line"; +import { Lists } from "./lists"; describe("Mesh unit tests", () => { - let geometryHelper = new GeometryHelper(); - let math = new MathBitByBit(); - let vector = new Vector(math, geometryHelper); - let transforms = new Transforms(vector, math); - let point = new Point(geometryHelper, transforms, vector); - let polyline = new Polyline(vector, point, geometryHelper); - let meshBitByBit = new MeshBitByBit(vector, polyline); - - const TOLERANCE = 1e-7; - - const expectPointCloseTo = ( - received: Inputs.Base.Point3 | Inputs.Base.Vector3 | undefined, - expected: Inputs.Base.Point3 | Inputs.Base.Vector3, - precision = TOLERANCE - ) => { - expect(received).toBeDefined(); - if (!received) return; - expect(received.length).toEqual(expected.length); - expect(received[0]).toBeCloseTo(expected[0], precision); - expect(received[1]).toBeCloseTo(expected[1], precision); - if (expected.length > 2 && received.length > 2) { - expect(received[2]).toBeCloseTo(expected[2], precision); - } - }; - const expectSegmentCloseTo = ( - received: Inputs.Base.Segment3 | undefined, - expected: Inputs.Base.Segment3, - precision = TOLERANCE - ) => { - expect(received).toBeDefined(); - if (!received) return; - expect(received).toHaveLength(2); - const order1Matches = Math.abs(vector.dist({ first: received[0], second: expected[0] })) < precision && - Math.abs(vector.dist({ first: received[1], second: expected[1] })) < precision; - const order2Matches = Math.abs(vector.dist({ first: received[0], second: expected[1] })) < precision && - Math.abs(vector.dist({ first: received[1], second: expected[0] })) < precision; - expect(order1Matches || order2Matches).toBe(true); - }; - - const expectPlaneCloseTo = ( - received: Inputs.Base.TrianglePlane3 | undefined, - expected: Inputs.Base.TrianglePlane3, - precision = TOLERANCE - ) => { - expect(received).toBeDefined(); - if (!received) return; - const normalDir1 = vector.sub({ first: received.normal, second: expected.normal }); - const normalDir2 = vector.add({ first: received.normal, second: expected.normal }); - const dir1Match = vector.lengthSq({ vector: normalDir1 as Inputs.Base.Vector3 }) < precision * precision; - const dir2Match = vector.lengthSq({ vector: normalDir2 as Inputs.Base.Vector3 }) < precision * precision; - expect(dir1Match || dir2Match).toBe(true); - expect(received.d).toBeCloseTo(dir1Match ? expected.d : -expected.d, precision); - }; + const uh = new UnitTestHelper(); + + let geometryHelper: GeometryHelper; + let math: MathBitByBit; + let vector: Vector; + let transforms: Transforms; + let point:Point; + let line: Line; + let lists: Lists; + let polyline: Polyline; + let meshBitByBit: MeshBitByBit; + beforeAll(() => { geometryHelper = new GeometryHelper(); math = new MathBitByBit(); vector = new Vector(math, geometryHelper); transforms = new Transforms(vector, math); - point = new Point(geometryHelper, transforms, vector); - polyline = new Polyline(vector, point, geometryHelper); + lists = new Lists(); + point = new Point(geometryHelper, transforms, vector, lists); + line = new Line(vector, point, geometryHelper); + polyline = new Polyline(vector, point, line, geometryHelper); meshBitByBit = new MeshBitByBit(vector, polyline); }); // Simple plane definitions const xyPlane: Inputs.Base.TrianglePlane3 = { normal: [0, 0, 1], d: 0 }; // Z=0 plane const xyPlaneOffset: Inputs.Base.TrianglePlane3 = { normal: [0, 0, 1], d: 5 }; // Z=5 plane - const slantedPlane: Inputs.Base.TrianglePlane3 = { normal: vector.normalized({ vector: [1, 1, 1] }) as Inputs.Base.Vector3, d: 0 }; // X+Y+Z=0 plane through origin + const slantedPlane: Inputs.Base.TrianglePlane3 = { normal: uh.vector.normalized({ vector: [1, 1, 1] }) as Inputs.Base.Vector3, d: 0 }; // X+Y+Z=0 plane through origin // Simple triangles const triXY1: Inputs.Base.Triangle3 = [[0, 0, 0], [1, 0, 0], [0, 1, 0]]; // On XY plane, normal ~[0,0,1] @@ -121,18 +85,18 @@ describe("Mesh unit tests", () => { describe("calculateTrianglePlane", () => { it("should calculate plane for simple XY triangle", () => { const plane = meshBitByBit.calculateTrianglePlane({ triangle: triXY1 }); - expectPlaneCloseTo(plane, xyPlane); + uh.expectPlaneCloseTo(plane, xyPlane); }); it("should calculate plane for offset XY triangle", () => { const plane = meshBitByBit.calculateTrianglePlane({ triangle: triXYOffset }); - expectPlaneCloseTo(plane, xyPlaneOffset); + uh.expectPlaneCloseTo(plane, xyPlaneOffset); }); it("should calculate plane for XZ triangle", () => { const plane = meshBitByBit.calculateTrianglePlane({ triangle: triXZ }); const expectedPlane: Inputs.Base.TrianglePlane3 = { normal: [0, -1, 0], d: 0 }; - expectPlaneCloseTo(plane, expectedPlane); + uh.expectPlaneCloseTo(plane, expectedPlane); }); it("should calculate plane for slanted triangle", () => { @@ -140,7 +104,7 @@ describe("Mesh unit tests", () => { const expectedNormal = vector.normalized({ vector: [1, 1, 1] }) as Inputs.Base.Vector3; const expectedD = vector.dot({ first: expectedNormal, second: [1, 0, 0] }); const expectedPlane: Inputs.Base.TrianglePlane3 = { normal: expectedNormal, d: expectedD }; - expectPlaneCloseTo(plane, expectedPlane); + uh.expectPlaneCloseTo(plane, expectedPlane); }); it("should return undefined for degenerate collinear triangle", () => { @@ -170,7 +134,7 @@ describe("Mesh unit tests", () => { expect(plane).toBeDefined(); if (plane) { const normalMagnitude = Math.sign(plane.normal[2]); - expectPointCloseTo(plane.normal, [0, 0, 1 * normalMagnitude], TOLERANCE); + uh.expectPointCloseTo(plane.normal, [0, 0, 1 * normalMagnitude]); expect(plane.d).toBeCloseTo(0, TOLERANCE); } }); @@ -229,21 +193,21 @@ describe("Mesh unit tests", () => { it("should return correct segment for simple orthogonal intersection (XY and XZ)", () => { const expectedSegment: Inputs.Base.Segment3 = [[0, 0, 0], [1, 0, 0]]; const result = meshBitByBit.triangleTriangleIntersection({ triangle1: triXY1, triangle2: triXZ }); - expectSegmentCloseTo(result, expectedSegment); + uh.expectSegmentCloseTo(result, expectedSegment); }); it("should return correct segment for shifted orthogonal intersection", () => { const triXZ_Shifted: Inputs.Base.Triangle3 = [[0.5, 0, 0], [1.5, 0, 0], [0.5, 0, 1]]; // Y=0 plane, X from 0.5 to 1.5 const expectedSegment: Inputs.Base.Segment3 = [[0.5, 0, 0], [1.0, 0, 0]]; const result = meshBitByBit.triangleTriangleIntersection({ triangle1: triXY1, triangle2: triXZ_Shifted }); - expectSegmentCloseTo(result, expectedSegment); + uh.expectSegmentCloseTo(result, expectedSegment); }); it("should return correct segment for piercing intersection", () => { const triPiercing: Inputs.Base.Triangle3 = [[1, -1, -1], [1, 1, 1], [1, 3, -1]]; // On X=1 plane const expectedSegment: Inputs.Base.Segment3 = [[1, 0, 0], [1, 1, 0]]; const result = meshBitByBit.triangleTriangleIntersection({ triangle1: [[0, 0, 0], [2, 0, 0], [0, 2, 0]], triangle2: triPiercing }); - expectSegmentCloseTo(result, expectedSegment); + uh.expectSegmentCloseTo(result, expectedSegment); }); }); diff --git a/packages/dev/base/lib/api/services/mesh.ts b/packages/dev/base/lib/api/services/mesh.ts index 9155cf58..68acb5f5 100644 --- a/packages/dev/base/lib/api/services/mesh.ts +++ b/packages/dev/base/lib/api/services/mesh.ts @@ -39,7 +39,7 @@ export class MeshBitByBit { return undefined; // Degenerate triangle } - // Defensive copy if normalize modifies in-place, otherwise remove .slice() + // Defensive copy if normalize modifies in-place const normalizedNormal = this.vector.normalized({ vector: normal }) as Inputs.Base.Vector3; const d = this.vector.dot({ first: normalizedNormal, second: inputs.triangle[0] }); return { normal: normalizedNormal, d: d }; @@ -91,7 +91,6 @@ export class MeshBitByBit { const allDistQZero = distQ_Plane1.every(d => Math.abs(d) < EPSILON); if (allDistPZero && allDistQZero) { - // console.warn("Coplanar case detected, not handled."); return undefined; // Explicitly not handling coplanar intersection areas } @@ -99,7 +98,6 @@ export class MeshBitByBit { const det = this.vector.dot({ first: lineDir, second: lineDir }); // det = |lineDir|^2 if (det < EPSILON * EPSILON) { - // console.warn("Planes are parallel or near parallel."); return undefined; // Planes parallel, no line intersection (coplanar case handled above) } @@ -149,8 +147,7 @@ export class MeshBitByBit { if (t1_intersection_points_3d.length < 2 || t2_intersection_points_3d.length < 2) { // This can happen if triangles touch at a vertex or edge without crossing planes, // or due to numerical precision near edges/vertices. - // console.log("Intersection appears to be edge/vertex contact or numerical issue."); - return undefined; // Treat touch as no intersection segment for now + return undefined; // Treat touch as no intersection segment } // Calculate a robust origin ON the intersection line @@ -167,7 +164,6 @@ export class MeshBitByBit { // Project the 3D intersection points onto the lineDir, relative to lineOrigin - // param = dot(point - lineOrigin, lineDir) const t1_params = t1_intersection_points_3d.map(p => this.vector.dot({ first: this.vector.sub({ first: p, second: lineOrigin }), second: lineDir }) ); @@ -244,6 +240,25 @@ export class MeshBitByBit { return this.polyline.sortSegmentsIntoPolylines({ segments, tolerance: inputs.tolerance }); } + /** + * Computes the intersection points of two meshes. + * @param inputs first mesh, second mesh, and tolerance + * @returns array of intersection points + * @group mesh + * @shortname mesh-mesh int points + * @drawable false + */ + meshMeshIntersectionPoints(inputs: Inputs.Mesh.MeshMeshToleranceDto): Inputs.Base.Point3[][] { + const polylines = this.meshMeshIntersectionPolylines(inputs); + return polylines.map(polyline => { + if(polyline.isClosed){ + return [...polyline.points, polyline.points[0]]; + } else { + return polyline.points; + } + }); + } + private computeIntersectionPoint(u: Inputs.Base.Point3, v: Inputs.Base.Point3, t: number) { return this.vector.add( { diff --git a/packages/dev/base/lib/api/services/point.test.ts b/packages/dev/base/lib/api/services/point.test.ts index 339a1482..091d686e 100644 --- a/packages/dev/base/lib/api/services/point.test.ts +++ b/packages/dev/base/lib/api/services/point.test.ts @@ -4,12 +4,17 @@ import { Point } from "./point"; import { Transforms } from "./transforms"; import { Vector } from "./vector"; import * as Inputs from "../inputs"; +import { UnitTestHelper } from "../unit-test-helper"; +import { Lists } from "./lists"; describe("Point unit tests", () => { + const uh = new UnitTestHelper(); + let geometryHelper: GeometryHelper; let math: MathBitByBit; let vector: Vector; let point: Point; + let lists: Lists; let transforms: Transforms; beforeAll(() => { @@ -17,11 +22,10 @@ describe("Point unit tests", () => { math = new MathBitByBit(); vector = new Vector(math, geometryHelper); transforms = new Transforms(vector, math); - point = new Point(geometryHelper, transforms, vector); + lists = new Lists(); + point = new Point(geometryHelper, transforms, vector, lists); }); - const TOLERANCE = 1e-7; - describe("Point Class Unit Tests (Integration)", () => { describe("transformPoint", () => { @@ -30,7 +34,7 @@ describe("Point unit tests", () => { const translationVec: Inputs.Base.Vector3 = [10, -5, 2]; const transformation = transforms.translationXYZ({ translation: translationVec }); const result = point.transformPoint({ point: p, transformation }); - expectPointCloseTo(result, [11, -3, 5]); + uh.expectPointCloseTo(result, [11, -3, 5]); }); it("should rotate a point around Z axis", () => { @@ -41,7 +45,7 @@ describe("Point unit tests", () => { angle: 90 }); const result = point.transformPoint({ point: p, transformation }); - expectPointCloseTo(result, [0, 1, 5]); + uh.expectPointCloseTo(result, [0, 1, 5]); }); it("should scale a point relative to origin", () => { @@ -51,7 +55,7 @@ describe("Point unit tests", () => { scaleXyz: [2, 0.5, 1] }); const result = point.transformPoint({ point: p, transformation }); - expectPointCloseTo(result, [4, 1.5, 4]); + uh.expectPointCloseTo(result, [4, 1.5, 4]); }); }); @@ -61,7 +65,7 @@ describe("Point unit tests", () => { const translationVec: Inputs.Base.Vector3 = [5, -5, 0]; const transformation = transforms.translationXYZ({ translation: translationVec }); const result = point.transformPoints({ points: pts, transformation }); - expectPointsCloseTo(result, [[6, -3, 3], [15, 5, 10]]); + uh.expectPointsCloseTo(result, [[6, -3, 3], [15, 5, 10]]); }); it("should rotate multiple points", () => { @@ -72,7 +76,7 @@ describe("Point unit tests", () => { angle: -90 }); const result = point.transformPoints({ points: pts, transformation }); - expectPointsCloseTo(result, [[0, -1, 0], [1, 0, 5]]); + uh.expectPointsCloseTo(result, [[0, -1, 0], [1, 0, 5]]); }); it("should handle empty points array", () => { @@ -91,7 +95,7 @@ describe("Point unit tests", () => { transforms.translationXYZ({ translation: [1, 1, 1] }) ]; const result = point.transformsForPoints({ points: pts, transformation: transformations }); - expectPointsCloseTo(result, [[0, 1, 0], [6, 6, 6]]); + uh.expectPointsCloseTo(result, [[0, 1, 0], [6, 6, 6]]); }); it("should throw an error if points and transformations lengths differ", () => { @@ -115,7 +119,7 @@ describe("Point unit tests", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [1, -1, 2]]; const translation: Inputs.Base.Vector3 = [10, 20, 30]; const result = point.translatePoints({ points: pts, translation }); - expectPointsCloseTo(result, [[10, 20, 30], [11, 19, 32]]); + uh.expectPointsCloseTo(result, [[10, 20, 30], [11, 19, 32]]); }); }); @@ -124,7 +128,7 @@ describe("Point unit tests", () => { const pts: Inputs.Base.Point3[] = [[1, 1, 1], [5, 5, 5]]; const translations: Inputs.Base.Vector3[] = [[10, 0, 0], [0, 20, 0]]; const result = point.translatePointsWithVectors({ points: pts, translations }); - expectPointsCloseTo(result, [[11, 1, 1], [5, 25, 5]]); + uh.expectPointsCloseTo(result, [[11, 1, 1], [5, 25, 5]]); }); it("should throw an error if points and translations lengths differ", () => { @@ -141,33 +145,32 @@ describe("Point unit tests", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [1, -1, 2]]; const x = -1, y = 2, z = -3; const result = point.translateXYZPoints({ points: pts, x, y, z }); - expectPointsCloseTo(result, [[-1, 2, -3], [0, 1, -1]]); + uh.expectPointsCloseTo(result, [[-1, 2, -3], [0, 1, -1]]); }); }); describe("scalePointsCenterXYZ", () => { it("should scale points relative to a center", () => { const pts: Inputs.Base.Point3[] = [[2, 2, 2], [0, 0, 0]]; - const center: Inputs.Base.Point3 = [1, 1, 1]; // Center of scaling + const center: Inputs.Base.Point3 = [1, 1, 1]; const scaleXyz: Inputs.Base.Vector3 = [2, 3, 0.5]; const result = point.scalePointsCenterXYZ({ points: pts, center, scaleXyz }); - expectPointsCloseTo(result, [[3, 4, 1.5], [-1, -2, 0.5]]); + uh.expectPointsCloseTo(result, [[3, 4, 1.5], [-1, -2, 0.5]]); }); }); describe("rotatePointsCenterAxis", () => { it("should rotate points around a center and axis", () => { - const pts: Inputs.Base.Point3[] = [[2, 1, 5], [1, 1, 0]]; // P1 is offset from center - const center: Inputs.Base.Point3 = [1, 1, 5]; // Center of rotation - const axis: Inputs.Base.Vector3 = [0, 0, 1]; // Z-axis + const pts: Inputs.Base.Point3[] = [[2, 1, 5], [1, 1, 0]]; + const center: Inputs.Base.Point3 = [1, 1, 5]; + const axis: Inputs.Base.Vector3 = [0, 0, 1]; const angle = 90; const result = point.rotatePointsCenterAxis({ points: pts, center, axis, angle }); - expectPointsCloseTo(result, [[1, 2, 5], [1, 1, 0]]); + uh.expectPointsCloseTo(result, [[1, 2, 5], [1, 1, 0]]); }); }); describe("boundingBoxOfPoints", () => { - // These tests don't rely on external dependencies, same as before it("should calculate the correct bounding box for multiple points", () => { const points: Inputs.Base.Point3[] = [[1, 2, 3], [4, -1, 6], [0, 5, -2]]; const expectedBBox: Inputs.Base.BoundingBox = { @@ -181,7 +184,7 @@ describe("Point unit tests", () => { const result = point.boundingBoxOfPoints({ points }); expect(result.min).toEqual(expectedBBox.min); expect(result.max).toEqual(expectedBBox.max); - expectPointCloseTo(result.center, expectedBBox.center); + uh.expectPointCloseTo(result.center, expectedBBox.center); expect(result.width).toBeCloseTo(expectedBBox.width); expect(result.height).toBeCloseTo(expectedBBox.height); expect(result.length).toBeCloseTo(expectedBBox.length); @@ -214,13 +217,12 @@ describe("Point unit tests", () => { }); describe("closestPointFromPoints methods", () => { - // These tests rely only on distance, which is internal or uses basic math const sourcePoint: Inputs.Base.Point3 = [0, 0, 0]; const targetPoints: Inputs.Base.Point3[] = [ - [10, 0, 0], // dist 10 - [0, 5, 0], // dist 5 - closest - [0, 0, -7], // dist 7 - [3, 4, 0], // dist 5 - first one at index 1 (-> 2) should be picked + [10, 0, 0], + [0, 5, 0], + [0, 0, -7], + [3, 4, 0], ]; const expectedClosestPoint: Inputs.Base.Point3 = [0, 5, 0]; const expectedClosestIndex = 2; // 1-based index @@ -331,17 +333,14 @@ describe("Point unit tests", () => { describe("averagePoint", () => { it("should calculate the average of multiple points", () => { const pts: Inputs.Base.Point3[] = [[1, 1, 1], [3, 5, -1], [5, 0, 6]]; - // AvgX = (1+3+5)/3 = 9/3 = 3 - // AvgY = (1+5+0)/3 = 6/3 = 2 - // AvgZ = (1-1+6)/3 = 6/3 = 2 const result = point.averagePoint({ points: pts }); - expectPointCloseTo(result, [3, 2, 2]); + uh.expectPointCloseTo(result, [3, 2, 2]); }); it("should return the point itself if only one point is provided", () => { const pts: Inputs.Base.Point3[] = [[10, 20, 30]]; const result = point.averagePoint({ points: pts }); - expectPointCloseTo(result, [10, 20, 30]); + uh.expectPointCloseTo(result, [10, 20, 30]); }); it("should return NaN components if points array is empty", () => { @@ -408,7 +407,7 @@ describe("Point unit tests", () => { it("should generate points on XY plane by default", () => { const result = point.hexGrid({ radiusHexagon: 1, nrHexagonsX: 2, nrHexagonsY: 2, orientOnCenter: false, pointsOnGround: false }); expect(result.length).toBe(4); - result.forEach(p => expect(p[2]).toBe(0)); // Z should be 0 + result.forEach(p => expect(p[2]).toBe(0)); }); it("should place points on XZ plane if pointsOnGround is true", () => { @@ -435,7 +434,7 @@ describe("Point unit tests", () => { expect(avgX).toBeCloseTo(0.577); expect(avgY).toBeCloseTo(0); - expect(avgZ).toBeCloseTo(0); + expect(avgZ).toBeCloseTo(0); expect(resultCenter).not.toEqual(resultNoCenter); }); @@ -446,43 +445,43 @@ describe("Point unit tests", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [0, 0, 1e-9], [1, 1, 1], [1, 1, 1 + 1e-4], [2, 2, 2]]; const tolerance = 1e-5; const result = point.removeConsecutiveDuplicates({ points: pts, tolerance, checkFirstAndLast: false }); - expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [1, 1, 1 + 1e-4], [2, 2, 2]]); + uh.expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [1, 1, 1 + 1e-4], [2, 2, 2]]); }); it("should remove consecutive duplicate points with default tolerance", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [0, 0, 1e-8], [1, 1, 1], [1, 1, 1 + 1e-3], [2, 2, 2]]; const result = point.removeConsecutiveDuplicates({ points: pts, tolerance: undefined, checkFirstAndLast: false }); - expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [1, 1, 1 + 1e-3], [2, 2, 2]]); + uh.expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [1, 1, 1 + 1e-3], [2, 2, 2]]); }); it("should keep all points if no consecutive duplicates exist", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]]; const result = point.removeConsecutiveDuplicates({ points: pts, tolerance: 1e-5, checkFirstAndLast: false }); - expectPointsCloseTo(result, pts); + uh.expectPointsCloseTo(result, pts); }); it("should handle checkFirstAndLast correctly for open polyline", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [1, 1, 1], [2, 2, 2], [0, 0, 1e-9]]; const result = point.removeConsecutiveDuplicates({ points: pts, checkFirstAndLast: true, tolerance: 1e-5 }); - expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [2, 2, 2]]); + uh.expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [2, 2, 2]]); }); it("should handle checkFirstAndLast correctly for closed polyline (already duplicated)", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [1, 1, 1], [2, 2, 2], [0, 0, 0]]; const result = point.removeConsecutiveDuplicates({ points: pts, checkFirstAndLast: true, tolerance: 1e-5 }); - expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [2, 2, 2]]); + uh.expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [2, 2, 2]]); }); it("should handle checkFirstAndLast=false", () => { const pts: Inputs.Base.Point3[] = [[0, 0, 0], [1, 1, 1], [2, 2, 2], [0, 0, 1e-9]]; // Last point close to first const result = point.removeConsecutiveDuplicates({ points: pts, checkFirstAndLast: false, tolerance: 1e-5 }); - expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [2, 2, 2], [0, 0, 1e-9]]); + uh.expectPointsCloseTo(result, [[0, 0, 0], [1, 1, 1], [2, 2, 2], [0, 0, 1e-9]]); }); it("should handle single point array", () => { const pts: Inputs.Base.Point3[] = [[1, 2, 3]]; const result = point.removeConsecutiveDuplicates({ points: pts, tolerance: 1e-5, checkFirstAndLast: false }); - expectPointsCloseTo(result, pts); + uh.expectPointsCloseTo(result, pts); }); it("should handle empty point array", () => { @@ -498,23 +497,23 @@ describe("Point unit tests", () => { const p2: Inputs.Base.Point3 = [1, 0, 0]; const p3: Inputs.Base.Point3 = [0, 1, 0]; const normal = point.normalFromThreePoints({ point1: p1, point2: p2, point3: p3, reverseNormal: false }); - expectPointCloseTo(normal, [0, 0, 1]); + uh.expectPointCloseTo(normal, [0, 0, 1]); }); it("should calculate the normal vector for non-collinear points (off-axis)", () => { const p1: Inputs.Base.Point3 = [1, 1, 1]; - const p2: Inputs.Base.Point3 = [2, 1, 1]; // P2-P1 = [1,0,0] - const p3: Inputs.Base.Point3 = [1, 2, 1]; // P3-P1 = [0,1,0] + const p2: Inputs.Base.Point3 = [2, 1, 1]; + const p3: Inputs.Base.Point3 = [1, 2, 1]; const normal = point.normalFromThreePoints({ point1: p1, point2: p2, point3: p3, reverseNormal: false }); - expectPointCloseTo(normal, [0, 0, 1]); + uh.expectPointCloseTo(normal, [0, 0, 1]); }); it("should calculate the normal vector for points forming XZ plane", () => { const p1: Inputs.Base.Point3 = [0, 0, 0]; - const p2: Inputs.Base.Point3 = [1, 0, 0]; // V1=[1,0,0] - const p3: Inputs.Base.Point3 = [0, 0, 1]; // V2=[0,0,1] + const p2: Inputs.Base.Point3 = [1, 0, 0]; + const p3: Inputs.Base.Point3 = [0, 0, 1]; const normal = point.normalFromThreePoints({ point1: p1, point2: p2, point3: p3, reverseNormal: false }); - expectPointCloseTo(normal, [0, -1, 0]); + uh.expectPointCloseTo(normal, [0, -1, 0]); }); it("should reverse the normal if reverseNormal is true", () => { @@ -522,16 +521,13 @@ describe("Point unit tests", () => { const p2: Inputs.Base.Point3 = [1, 0, 0]; const p3: Inputs.Base.Point3 = [0, 1, 0]; const normal = point.normalFromThreePoints({ point1: p1, point2: p2, point3: p3, reverseNormal: true }); - expectPointCloseTo(normal, [0, 0, -1]); + uh.expectPointCloseTo(normal, [0, 0, -1]); }); it("should return undefined for collinear points", () => { const p1: Inputs.Base.Point3 = [0, 0, 0]; const p2: Inputs.Base.Point3 = [1, 1, 1]; - const p3: Inputs.Base.Point3 = [2, 2, 2]; // p3 = p1 + 2*(p2-p1) - // V1 = [1,1,1], V2 = [2,2,2] - // Cross = [1*2-1*2, 1*2-1*2, 1*2-1*2] = [0,0,0] - // Should log warning and return undefined + const p3: Inputs.Base.Point3 = [2, 2, 2]; const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(); const normal = point.normalFromThreePoints({ point1: p1, point2: p2, point3: p3, reverseNormal: false }); expect(normal).toBeUndefined(); @@ -581,7 +577,6 @@ describe("Point unit tests", () => { }); it("should return false for points equal to tolerance distance", () => { - // distance < tolerance, so if distance === tolerance, should be false const p1: Inputs.Base.Point3 = [1, 1, 1]; const p2: Inputs.Base.Point3 = [1, 1, 1 + 1e-5]; const tolerance = 1e-5; @@ -591,27 +586,269 @@ describe("Point unit tests", () => { }); - // Helper to compare points/vectors with tolerance - const expectPointCloseTo = ( - received: Inputs.Base.Point3 | Inputs.Base.Vector3 | undefined, - expected: Inputs.Base.Point3 | Inputs.Base.Vector3 - ) => { - expect(received).toBeDefined(); - if (!received) return; // Guard for TS - expect(received.length).toEqual(expected.length); - expect(received[0]).toBeCloseTo(expected[0], TOLERANCE); - expect(received[1]).toBeCloseTo(expected[1], TOLERANCE); - if (expected.length > 2 && received.length > 2) { - expect(received[2]).toBeCloseTo(expected[2], TOLERANCE); - } - }; - - // Helper to compare arrays of points/vectors with tolerance - const expectPointsCloseTo = ( - received: Inputs.Base.Point3[] | Inputs.Base.Vector3[], - expected: Inputs.Base.Point3[] | Inputs.Base.Vector3[] - ) => { - expect(received.length).toEqual(expected.length); - received.forEach((p, i) => expectPointCloseTo(p, expected[i])); - }; + describe("PointService.sortPoints", () => { + + it("should return an empty array when given an empty array", () => { + const input: Inputs.Point.PointsDto = { points: [] }; + const result = point.sortPoints(input); + expect(result).toEqual([]); + }); + + it("should return the same array when given an array with a single point", () => { + const pt: Inputs.Base.Point3 = [1, 2, 3]; + const input: Inputs.Point.PointsDto = { points: [pt] }; + const result = point.sortPoints(input); + expect(result).toEqual([pt]); + }); + + it("should not modify the original input array (immutability)", () => { + const originalPoints: Inputs.Base.Point3[] = [[3, 0, 0], [1, 0, 0], [2, 0, 0]]; + const input: Inputs.Point.PointsDto = { points: originalPoints }; + const originalCopy = [...originalPoints]; + const result = point.sortPoints(input); + expect(result).not.toBe(originalPoints); + expect(originalPoints).toEqual(originalCopy); + }); + + it("should correctly sort points primarily by the X coordinate", () => { + const input: Inputs.Point.PointsDto = { points: [[3, 0, 0], [1, 5, 5], [2, -1, -1]] }; + const expected: Inputs.Base.Point3[] = [[1, 5, 5], [2, -1, -1], [3, 0, 0]]; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + + it("should correctly sort points with the same X coordinate by the Y coordinate", () => { + const input: Inputs.Point.PointsDto = { points: [[1, 5, 0], [2, 0, 0], [1, 2, 5], [1, 8, -1]] }; + const expected: Inputs.Base.Point3[] = [[1, 2, 5], [1, 5, 0], [1, 8, -1], [2, 0, 0]]; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + + it("should correctly sort points with the same X and Y coordinates by the Z coordinate", () => { + const input: Inputs.Point.PointsDto = { points: [[1, 5, 10], [2, 0, 0], [1, 5, -2], [1, 5, 0]] }; + const expected: Inputs.Base.Point3[] = [[1, 5, -2], [1, 5, 0], [1, 5, 10], [2, 0, 0]]; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + + it("should handle a mix of points with various coordinates including negatives", () => { + const input: Inputs.Point.PointsDto = { + points: [ + [2, 1, 5], [1, 5, 0], [2, 1, 0], [-1, 10, 0], [1, 0, 0], [2, 0, 0], [-1, -5, 10] + ] + }; + const expected: Inputs.Base.Point3[] = [ + [-1, -5, 10], [-1, 10, 0], [1, 0, 0], [1, 5, 0], [2, 0, 0], [2, 1, 0], [2, 1, 5] + ]; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + + it("should handle already sorted points correctly", () => { + const expected: Inputs.Base.Point3[] = [[1, 2, 3], [1, 2, 4], [1, 3, 0], [2, 0, 0]]; + const input: Inputs.Point.PointsDto = { points: [...expected] }; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + + it("should handle reverse sorted points correctly", () => { + const input: Inputs.Point.PointsDto = { points: [[3, 0, 0], [2, 0, 0], [1, 0, 0]] }; + const expected: Inputs.Base.Point3[] = [[1, 0, 0], [2, 0, 0], [3, 0, 0]]; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + + it("should handle duplicate points", () => { + const input: Inputs.Point.PointsDto = { points: [[1, 1, 1], [0, 0, 0], [2, 2, 2], [0, 0, 0], [1, 1, 1]] }; + const expected: Inputs.Base.Point3[] = [[0, 0, 0], [0, 0, 0], [1, 1, 1], [1, 1, 1], [2, 2, 2]]; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + + it("should handle floating point numbers correctly", () => { + const input: Inputs.Point.PointsDto = { points: [[1.5, 0.1, 0], [1.1, 0.2, 0], [1.5, 0, 0], [1.1, 0.1, 0]] }; + const expected: Inputs.Base.Point3[] = [[1.1, 0.1, 0], [1.1, 0.2, 0], [1.5, 0, 0], [1.5, 0.1, 0]]; + const result = point.sortPoints(input); + expect(result).toEqual(expected); + }); + }); + + describe("calculateMaxFilletRadius", () => { + const defaultTolerance = 1e-7; // Default tolerance for the function if not provided + const precision = 6; // Decimal places for toBeCloseTo assertions + + // Helper to create input object matching the confusing naming convention + const createInput = (p1: Inputs.Base.Point3, corner: Inputs.Base.Point3, p2: Inputs.Base.Point3, tolerance: number = defaultTolerance): Inputs.Point.ThreePointsToleranceDto => ({ + start: p1, + center: p2, + end: corner, + tolerance: tolerance + }); + + it("should calculate correct radius for a simple 90-degree corner (2D)", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 3, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadius(input)).toBeCloseTo(3.0, precision); + }); + + it("should calculate correct radius for a symmetric 90-degree corner (2D)", () => { + const geoP1: Inputs.Base.Point3 = [4, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 4, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadius(input)).toBeCloseTo(4.0, precision); + }); + + it("should calculate correct radius for an acute angle (60 degrees, 2D)", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [5 * Math.cos(Math.PI / 3), 5 * Math.sin(Math.PI / 3), 0]; + const input = createInput(geoP1, geoC, geoP2); + const expected = 5 * Math.tan(Math.PI / 6); + expect(point.maxFilletRadius(input)).toBeCloseTo(expected, precision); + }); + + it("should calculate correct radius for an obtuse angle (120 degrees, 2D)", () => { + const geoP1: Inputs.Base.Point3 = [4, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [6 * Math.cos(2 * Math.PI / 3), 6 * Math.sin(2 * Math.PI / 3), 0]; + const input = createInput(geoP1, geoC, geoP2); + const expected = 4 * Math.tan(Math.PI / 3); + expect(point.maxFilletRadius(input)).toBeCloseTo(expected, precision); + }); + + it("should return 0 for collinear points (0 degrees)", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [10, 0, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadius(input)).toBeCloseTo(0, precision); + }); + + it("should return 0 for collinear points (180 degrees)", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [-3, 0, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadius(input)).toBeCloseTo(0, precision); + }); + + it("should return 0 if one segment has near-zero length (P1=C)", () => { + const geoP1: Inputs.Base.Point3 = [defaultTolerance / 2, 0, 0]; // Very close to C + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 3, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadius(input)).toBeCloseTo(0, precision); + }); + + it("should return 0 if one segment has near-zero length (P2=C)", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, defaultTolerance / 3, 0]; // Very close to C + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadius(input)).toBeCloseTo(0, precision); + }); + + it("should use the provided tolerance if specified", () => { + const customTolerance = 1e-4; + const geoP1: Inputs.Base.Point3 = [customTolerance / 2, 0, 0]; // Near zero relative to custom tolerance + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 3, 0]; + const input = createInput(geoP1, geoC, geoP2, customTolerance); + // Expect 0 because len1 < customTolerance + expect(point.maxFilletRadius(input)).toBeCloseTo(0, precision); + + const geoP1_ok: Inputs.Base.Point3 = [customTolerance * 2, 0, 0]; // OK relative to custom tolerance + const input_ok = createInput(geoP1_ok, geoC, geoP2, customTolerance); + // Expect non-zero result here + expect(point.maxFilletRadius(input_ok)).toBeGreaterThan(0); + }); + + it("should return 0 if all points coincide", () => { + const geoP1: Inputs.Base.Point3 = [0, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 0, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadius(input)).toBeCloseTo(0, precision); + }); + + it("should calculate correct radius for a corner in 3D space", () => { + const geoP1: Inputs.Base.Point3 = [1, 1, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 2, 0]; + const input = createInput(geoP1, geoC, geoP2); + const expected = Math.sqrt(2) * Math.tan(Math.PI / 8); + expect(point.maxFilletRadius(input)).toBeCloseTo(expected, precision); + }); + }); + + describe("calculateMaxFilletRadiusHalfLine", () => { + const defaultTolerance = 1e-7; + const precision = 6; + + const createInput = (p1: Inputs.Base.Point3, corner: Inputs.Base.Point3, p2: Inputs.Base.Point3, tolerance: number = defaultTolerance): Inputs.Point.ThreePointsToleranceDto => ({ + start: p1, center: p2, end: corner, tolerance: tolerance + }); + + it("should calculate correct radius for a simple 90-degree corner (2D)", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 3, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(1.5, precision); + }); + + it("should calculate correct radius for a symmetric 90-degree corner (2D)", () => { + const geoP1: Inputs.Base.Point3 = [4, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 4, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(2.0, precision); + }); + + it("should calculate correct radius for an acute angle (60 degrees, 2D)", () => { + const geoP1: Inputs.Base.Point3 = [6, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [4 * Math.cos(Math.PI / 3), 4 * Math.sin(Math.PI / 3), 0]; + const input = createInput(geoP1, geoC, geoP2); + const expected = 2 * Math.tan(Math.PI / 6); + expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(expected, precision); + }); + + it("should calculate correct radius for an obtuse angle (120 degrees, 2D)", () => { + const geoP1: Inputs.Base.Point3 = [4, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [6 * Math.cos(2 * Math.PI / 3), 6 * Math.sin(2 * Math.PI / 3), 0]; + const input = createInput(geoP1, geoC, geoP2); + const expected = 2 * Math.tan(Math.PI / 3); + expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(expected, precision); + }); + + it("should return 0 for collinear points (0 degrees)", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [10, 0, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(0, precision); + }); + + it("should return 0 if one segment has near-zero length", () => { + const geoP1: Inputs.Base.Point3 = [5, 0, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, defaultTolerance / 3, 0]; + const input = createInput(geoP1, geoC, geoP2); + expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(0, precision); + }); + + it("should calculate correct radius for a corner in 3D space", () => { + const geoP1: Inputs.Base.Point3 = [1, 1, 0]; + const geoC: Inputs.Base.Point3 = [0, 0, 0]; + const geoP2: Inputs.Base.Point3 = [0, 2, 0]; + const input = createInput(geoP1, geoC, geoP2); + const expected = (Math.sqrt(2) / 2.0) * Math.tan(Math.PI / 8); + expect(point.maxFilletRadiusHalfLine(input)).toBeCloseTo(expected, precision); + }); + }); }); diff --git a/packages/dev/base/lib/api/services/point.ts b/packages/dev/base/lib/api/services/point.ts index f4b51a41..75eab2e8 100644 --- a/packages/dev/base/lib/api/services/point.ts +++ b/packages/dev/base/lib/api/services/point.ts @@ -2,6 +2,8 @@ import { GeometryHelper } from "./geometry-helper"; import * as Inputs from "../inputs"; import { Transforms } from "./transforms"; import { Vector } from "./vector"; +import * as Models from "../models"; +import { Lists } from "./lists"; /** * Contains various methods for points. Point in bitbybit is simply an array containing 3 numbers for [x, y, z]. @@ -11,7 +13,7 @@ import { Vector } from "./vector"; export class Point { - constructor(private readonly geometryHelper: GeometryHelper, private readonly transforms: Transforms, private readonly vector: Vector) { } + constructor(private readonly geometryHelper: GeometryHelper, private readonly transforms: Transforms, private readonly vector: Vector, private readonly lists: Lists) { } /** * Transforms the single point @@ -114,6 +116,19 @@ export class Point { return this.geometryHelper.transformControlPoints(scaleTransforms, inputs.points); } + /** + * Stretch multiple points by providing center point, direction and uniform scale factor + * @param inputs Contains points, center point, direction and scale factor + * @returns Stretched points + * @group transforms + * @shortname stretch points dir from center + * @drawable true + */ + stretchPointsDirFromCenter(inputs: Inputs.Point.StretchPointsDirFromCenterDto): Inputs.Base.Point3[] { + const stretchTransforms = this.transforms.stretchDirFromCenter({ center: inputs.center, scale: inputs.scale, direction: inputs.direction }); + return this.geometryHelper.transformControlPoints(stretchTransforms, inputs.points); + } + /** * Rotate multiple points by providing center point, axis and degrees of rotation * @param inputs Contains points, axis, center point and angle of rotation @@ -393,6 +408,551 @@ export class Point { return points; } + /** + * Creates a pointy-top or flat-top hexagon grid, scaling hexagons to fit specified dimensions exactly. + * Returns both center points and the vertices of each (potentially scaled) hexagon. + * Hexagons are ordered column-first, then row-first. + * @param inputs Information about the desired grid dimensions and hexagon counts. + * @returns An object containing the array of center points and an array of hexagon vertex arrays. + * @group create + * @shortname scaled hex grid to fit + * @drawable false + */ + hexGridScaledToFit(inputs: Inputs.Point.HexGridScaledToFitDto): Models.Point.HexGridData { + let width = inputs.width; + let height = inputs.height; + let nrHexagonsInHeight = inputs.nrHexagonsInHeight; + let nrHexagonsInWidth = inputs.nrHexagonsInWidth; + let extendTop = inputs.extendTop ?? false; + let extendBottom = inputs.extendBottom ?? false; + let extendLeft = inputs.extendLeft ?? false; + let extendRight = inputs.extendRight ?? false; + const { + + flatTop = false, + centerGrid = false, + pointsOnGround = false + } = inputs; + + // we flip the width and height if the hexagons are flat-topped and will then rotate resuls afterwards as default + // computes pointy-top hexagons + if (flatTop) { + const oldWidth = width; + width = inputs.height; + height = oldWidth; + const oldNrHexagonsInWidth = nrHexagonsInWidth; + nrHexagonsInWidth = nrHexagonsInHeight; + nrHexagonsInHeight = oldNrHexagonsInWidth; + const extendTopOld = extendTop; + const extendBottomOld = extendBottom; + const extendLeftOld = extendLeft; + const extendRightOld = extendRight; + extendTop = extendLeftOld; + extendBottom = extendRightOld; + extendLeft = extendBottomOld; + extendRight = extendTopOld; + } + + // --- Input Validation --- + if (width <= 0 || height <= 0 || nrHexagonsInWidth < 1 || nrHexagonsInHeight < 1) { + console.warn("Hex grid dimensions and counts must be positive."); + return { centers: [], hexagons: [], shortestDistEdge: undefined, longestDistEdge: undefined, maxFilletRadius: undefined }; + } + + // --- Generate Unscaled Regular Grid Centers (Radius = 1) --- + // Use the *existing* hexGrid function, ensuring it doesn't center or project yet. + const BASE_RADIUS = 1.0; + const unscaledCenters = this.hexGrid({ + radiusHexagon: BASE_RADIUS, + nrHexagonsX: nrHexagonsInWidth, + nrHexagonsY: nrHexagonsInHeight, + orientOnCenter: false, // Important: Do not center here + pointsOnGround: false // Keep on XY plane for now + }); + + if (unscaledCenters.length === 0) { + return { centers: [], hexagons: [], shortestDistEdge: undefined, longestDistEdge: undefined, maxFilletRadius: undefined }; // Return empty if base grid failed + } + + // --- Generate Unscaled Regular Hexagon Vertices (Radius = 1) --- + const unscaledHexagons: Inputs.Base.Point3[][] = unscaledCenters.map(center => + this.getRegularHexagonVertices(center, BASE_RADIUS) + ); + + // --- Determine Dimensions of the Unscaled Grid Bounding Box --- + let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; + for (const hex of unscaledHexagons) { + for (const vertex of hex) { + if (vertex[0] < minX) minX = vertex[0]; + if (vertex[0] > maxX) maxX = vertex[0]; + if (vertex[1] < minY) minY = vertex[1]; + if (vertex[1] > maxY) maxY = vertex[1]; + } + } + + const unscaledWidth = maxX - minX; + const unscaledHeight = maxY - minY; + + // --- Step 4: Calculate Scaling Factors --- + // Handle potential zero dimensions if only 1 hex (W/H would be based on hex size) + const scaleX = (unscaledWidth > 1e-9) ? width / unscaledWidth : 1; + const scaleY = (unscaledHeight > 1e-9) ? height / unscaledHeight : 1; + // If unscaled W/H is 0 (e.g., 1x1 grid), scale=1 means the final hex will have + // width/height derived from its regular R=1 shape, not fitting totalW/H. + // This might need adjustment if a single hex *must* fill the total W/H. + // For now, assume nrU/nrV > 1 or accept R=1 size for single hex. + + // --- Scale Centers and Vertices --- + // Scale relative to the min corner of the unscaled grid (minX, minY) + let scaledCenters: Inputs.Base.Point3[] = unscaledCenters.map(p => [ + (p[0] - minX) * scaleX, + (p[1] - minY) * scaleY, + 0 // Keep Z=0 for now + ]); + + let scaledHexagons: Inputs.Base.Point3[][] = unscaledHexagons.map(hex => + hex.map(v => [ + (v[0] - minX) * scaleX, + (v[1] - minY) * scaleY, + 0 // Keep Z=0 for now + ]) + ); + + let shortestDistEdge = Infinity; + let longestDistEdge = -Infinity; + let maxFilletRadius = 0; + + // --- Calculate Shortes/Longest & Extensions --- + if (scaledHexagons.length !== 0) { + const firstHex = scaledHexagons[0]; + maxFilletRadius = this.safestPointsMaxFilletHalfLine({ + points: firstHex, + checkLastWithFirst: true, + tolerance: 1e-7 + }); + // Calculate the shortest and longest edge distances + firstHex.forEach((pt, index) => { + const nextPt = firstHex[(index + 1) % firstHex.length]; + const dist = this.distance({ startPoint: pt, endPoint: nextPt }); + if (dist < shortestDistEdge) { + shortestDistEdge = dist; + } + if (dist > longestDistEdge) { + longestDistEdge = dist; + } + }); + + if (extendTop || extendBottom || extendLeft || extendRight) { + const pt1Pointy = firstHex[0]; + const pt2Pointy = firstHex[1]; + const cellHeight = pt1Pointy[1] - pt2Pointy[1]; + const cellWidth = pt2Pointy[0] - pt1Pointy[0]; + + if (extendTop && !extendBottom) { + const transform: Inputs.Point.StretchPointsDirFromCenterDto = { + center: [0, 0, 0], + direction: [0, 1, 0], + scale: height / (height - cellHeight), + }; + scaledHexagons = scaledHexagons.map(hex => { + transform.points = hex; + return this.stretchPointsDirFromCenter(transform); + }); + transform.points = scaledCenters; + scaledCenters = this.stretchPointsDirFromCenter(transform); + } + if (extendBottom && !extendTop) { + const transform: Inputs.Point.StretchPointsDirFromCenterDto = { + center: [0, height, 0], + direction: [0, -1, 0], + scale: height / (height - cellHeight), + }; + scaledHexagons = scaledHexagons.map(hex => { + transform.points = hex; + return this.stretchPointsDirFromCenter(transform); + }); + transform.points = scaledCenters; + scaledCenters = this.stretchPointsDirFromCenter(transform); + } + if (extendTop && extendBottom) { + const transform: Inputs.Point.StretchPointsDirFromCenterDto = { + center: [0, height / 2, 0], + direction: [0, 1, 0], + scale: height / (height - cellHeight * 2), + }; + scaledHexagons = scaledHexagons.map(hex => { + transform.points = hex; + return this.stretchPointsDirFromCenter(transform); + }); + transform.points = scaledCenters; + scaledCenters = this.stretchPointsDirFromCenter(transform); + } + if (extendLeft && !extendRight) { + const transform: Inputs.Point.StretchPointsDirFromCenterDto = { + center: [width, 0, 0], + direction: [1, 0, 0], + scale: width / (width - cellWidth), + }; + scaledHexagons = scaledHexagons.map(hex => { + transform.points = hex; + return this.stretchPointsDirFromCenter(transform); + }); + transform.points = scaledCenters; + scaledCenters = this.stretchPointsDirFromCenter(transform); + } + if (extendRight && !extendLeft) { + const transform: Inputs.Point.StretchPointsDirFromCenterDto = { + center: [0, 0, 0], + direction: [1, 0, 0], + scale: width / (width - cellWidth), + }; + scaledHexagons = scaledHexagons.map(hex => { + transform.points = hex; + return this.stretchPointsDirFromCenter(transform); + }); + transform.points = scaledCenters; + scaledCenters = this.stretchPointsDirFromCenter(transform); + } + if (extendLeft && extendRight) { + const transform: Inputs.Point.StretchPointsDirFromCenterDto = { + center: [width / 2, 0, 0], + direction: [1, 0, 0], + scale: width / (width - cellWidth * 2), + }; + scaledHexagons = scaledHexagons.map(hex => { + transform.points = hex; + return this.stretchPointsDirFromCenter(transform); + }); + transform.points = scaledCenters; + scaledCenters = this.stretchPointsDirFromCenter(transform); + } + } + } + + if (flatTop) { + // width and height are swapped + scaledCenters = this.rotatePointsCenterAxis({ + points: scaledCenters, + center: [width / 2, height / 2, 0], + axis: [0, 0, 1], + angle: 90 + }); + scaledHexagons = scaledHexagons.map(hex => { + return this.rotatePointsCenterAxis({ + points: hex, + center: [width / 2, height / 2, 0], + axis: [0, 0, 1], + angle: 90 + }); + }); + + // translate to new center + const vecTranslation = this.vector.sub({ + first: [height / 2, width / 2, 0], + second: [width / 2, height / 2, 0] + }) as Inputs.Base.Vector3; + scaledCenters = this.translatePoints({ + points: scaledCenters, + translation: vecTranslation + }); + scaledHexagons = scaledHexagons.map(hex => { + return this.translatePoints({ + points: hex, + translation: vecTranslation + }); + }); + } + + + // --- Apply Optional Centering --- + // Center the scaled grid (currently starting at [0,0]) around [0,0] + if (centerGrid) { + let shiftX = width / 2; + let shiftY = height / 2; + + if (flatTop) { + shiftX = height / 2; + shiftY = width / 2; + } + + for (let i = 0; i < scaledCenters.length; i++) { + scaledCenters[i][0] -= shiftX; + scaledCenters[i][1] -= shiftY; + } + for (let i = 0; i < scaledHexagons.length; i++) { + for (let j = 0; j < scaledHexagons[i].length; j++) { + scaledHexagons[i][j][0] -= shiftX; + scaledHexagons[i][j][1] -= shiftY; + } + } + } + + // --- Apply Optional Ground Projection --- + if (pointsOnGround) { + for (let i = 0; i < scaledCenters.length; i++) { + scaledCenters[i] = [scaledCenters[i][0], 0, scaledCenters[i][1]]; + } + for (let i = 0; i < scaledHexagons.length; i++) { + for (let j = 0; j < scaledHexagons[i].length; j++) { + scaledHexagons[i][j] = [scaledHexagons[i][j][0], 0, scaledHexagons[i][j][1]]; + } + } + } + + // We need to adjust orders to be column first and then row first if we choose flat top + if(flatTop){ + const grouped = this.lists.groupNth({ + list: scaledHexagons.reverse(), + nrElements: inputs.nrHexagonsInWidth, + keepRemainder: true, + }); + const res = this.lists.flipLists({ + list: grouped + }); + res.forEach(s => s.reverse()); + scaledHexagons = res.flat(); + + const groupedCenters = this.lists.groupNth({ + list: scaledCenters.reverse(), + nrElements: inputs.nrHexagonsInWidth, + keepRemainder: true, + }); + const resCenters = this.lists.flipLists({ + list: groupedCenters + }); + resCenters.forEach(s => s.reverse()); + scaledCenters = resCenters.flat(); + } + + // --- Return Result --- + return { + centers: scaledCenters, + hexagons: scaledHexagons, + shortestDistEdge, + longestDistEdge, + maxFilletRadius + }; + } + + /** + * Calculates the maximum possible fillet radius at a corner formed by two line segments + * sharing an endpoint (C), such that the fillet arc is tangent to both segments + * and lies entirely within them. + * @param inputs three points and the tolerance + * @returns the maximum fillet radius + * @group fillet + * @shortname max fillet radius + * @drawable false + */ + maxFilletRadius( + inputs: Inputs.Point.ThreePointsToleranceDto + ): number { + const { start: p1, center: p2, end: c, tolerance = 1e-7 } = inputs; + + const v1 = this.vector.sub({ first: p1, second: c }) as Inputs.Base.Vector3; + const v2 = this.vector.sub({ first: p2, second: c }) as Inputs.Base.Vector3; + + const len1 = this.vector.length({ vector: v1 }); + const len2 = this.vector.length({ vector: v2 }); + + if (len1 < tolerance || len2 < tolerance) { + return 0; + } + + const normV1 = this.vector.normalized({ vector: v1 }); + const normV2 = this.vector.normalized({ vector: v2 }); + if (!normV1 || !normV2) { + return 0; + } + + // Calculate the cosine of the angle between the vectors + // Clamp to [-1, 1] to avoid potential domain errors with acos due to floating point inaccuracies + const cosAlpha = Math.max(-1.0, Math.min(1.0, this.vector.dot({ first: normV1, second: normV2 }))); + + // Check for collinearity + // If vectors point in the same direction (angle ~ 0), no fillet + if (cosAlpha > 1.0 - tolerance) { + return 0; + } + // If vectors point in opposite directions (angle ~ 180 deg), no corner for a fillet + if (cosAlpha < -1.0 + tolerance) { + return 0; + } + + // Calculate the angle alpha (0 < alpha < PI) + const alpha = Math.acos(cosAlpha); + + // Calculate tan(alpha / 2) + // alpha/2 is between 0 and PI/2, so tan is positive and non-zero + const tanHalfAlpha = Math.tan(alpha / 2.0); + + // If tanHalfAlpha is extremely small (alpha near 0, shouldn't happen due to collinearity check), return 0 + if (tanHalfAlpha < tolerance) { + return 0; + } + + // The distance 'd' from corner C to the tangent point must be less than or equal to the segment lengths. + // d = r / tan(alpha/2) <= min(len1, len2) + // r <= min(len1, len2) * tan(alpha/2) + const maxRadius = Math.min(len1, len2) * tanHalfAlpha; + + return maxRadius; + } + + /** + * Calculates the maximum possible fillet radius at a corner C, such that the fillet arc + * is tangent to both segments (P1-C, P2-C) and the tangent points lie within + * the first half of each segment (measured from C). + * @param inputs three points and the tolerance + * @returns the maximum fillet radius + * @group fillet + * @shortname max fillet radius half line + * @drawable false + */ + maxFilletRadiusHalfLine( + inputs: Inputs.Point.ThreePointsToleranceDto + ): number { + const { start: p1, center: p2, end: c, tolerance = 1e-7 } = inputs; + + const v1 = this.vector.sub({ first: p1, second: c }) as Inputs.Base.Vector3; + const v2 = this.vector.sub({ first: p2, second: c }) as Inputs.Base.Vector3; + + const len1 = this.vector.length({ vector: v1 }); + const len2 = this.vector.length({ vector: v2 }); + + if (len1 < tolerance || len2 < tolerance) { + return 0; + } + + const normV1 = this.vector.normalized({ vector: v1 }); + const normV2 = this.vector.normalized({ vector: v2 }); + + if (!normV1 || !normV2) { + return 0; + } + + const cosAlpha = Math.max(-1.0, Math.min(1.0, this.vector.dot({ first: normV1, second: normV2 }))); + + if (cosAlpha > 1.0 - tolerance || cosAlpha < -1.0 + tolerance) { + return 0; // Collinear + } + + const alpha = Math.acos(cosAlpha); + const tanHalfAlpha = Math.tan(alpha / 2.0); + + if (tanHalfAlpha < tolerance) { + return 0; + } + + // The distance 'd' from corner C to the tangent point must be less than or equal + // to HALF the length of each segment. + // d = r / tan(alpha/2) <= min(len1 / 2, len2 / 2) + // r <= min(len1 / 2, len2 / 2) * tan(alpha/2) + const maxRadius = Math.min(len1 / 2.0, len2 / 2.0) * tanHalfAlpha; + + return maxRadius; + } + + /** + * Calculates the maximum possible fillet radius at each corner of a polyline formed by + * formed by a series of points. The fillet radius is calculated for each internal + * corner and optionally for the closing corners if the polyline is closed. + * @param inputs Points, checkLastWithFirst flag, and tolerance + * @returns Array of maximum fillet radii for each corner + * @group fillet + * @shortname max fillets half line + * @drawable false + */ + maxFilletsHalfLine( + inputs: Inputs.Point.PointsMaxFilletsHalfLineDto + ): number[] { + const { points, checkLastWithFirst = false, tolerance = 1e-7 } = inputs; + const n = points.length; + const results: number[] = []; + + // Need at least 3 points to form a corner + if (n < 3) { + return results; + } + + // 1. Calculate fillets for internal corners (P[1] to P[n-2]) + for (let i = 1; i < n - 1; i++) { + const p_prev = points[i - 1]; + const p_corner = points[i]; + const p_next = points[i + 1]; + + // Map geometric points to the DTO structure used by calculateMaxFilletRadiusHalfLine + // DTO: { start: P_prev, center: P_next, end: P_corner, tolerance } + const cornerInput: Inputs.Point.ThreePointsToleranceDto = { + start: p_prev, + center: p_next, + end: p_corner, + tolerance: tolerance + }; + results.push(this.maxFilletRadiusHalfLine(cornerInput)); + } + + // 2. Calculate fillets for closing corners if it's a closed polyline + if (checkLastWithFirst && n >= 3) { + // Corner at P[0] (formed by P[n-1]-P[0] and P[1]-P[0]) + const p_prev_start = points[n - 1]; // Previous point is the last point + const p_corner_start = points[0]; + const p_next_start = points[1]; + const startCornerInput: Inputs.Point.ThreePointsToleranceDto = { + start: p_prev_start, + center: p_next_start, + end: p_corner_start, + tolerance: tolerance + }; + results.push(this.maxFilletRadiusHalfLine(startCornerInput)); + + // Corner at P[n-1] (formed by P[n-2]-P[n-1] and P[0]-P[n-1]) + const p_prev_end = points[n - 2]; + const p_corner_end = points[n - 1]; + const p_next_end = points[0]; // Next point wraps around to the first point + const endCornerInput: Inputs.Point.ThreePointsToleranceDto = { + start: p_prev_end, + center: p_next_end, + end: p_corner_end, + tolerance: tolerance + }; + results.push(this.maxFilletRadiusHalfLine(endCornerInput)); + } + + return results; + } + + /** + * Calculates the single safest maximum fillet radius that can be applied + * uniformly to all corners of collection of points, based on the 'half-line' constraint. + * This is determined by finding the minimum of the maximum possible fillet + * radii calculated for each individual corner. + * @param inputs Defines the points, whether it's closed, and an optional tolerance. + * @returns The smallest value from the results of pointsMaxFilletsHalfLine. + * Returns 0 if the polyline has fewer than 3 points or if any + * calculated maximum radius is 0. + * @group fillet + * @shortname safest fillet radii points + * @drawable false + */ + safestPointsMaxFilletHalfLine( + inputs: Inputs.Point.PointsMaxFilletsHalfLineDto + ): number { + const allMaxRadii = this.maxFilletsHalfLine(inputs); + + if (allMaxRadii.length === 0) { + // No corners, or fewer than 3 points. No fillet possible. + return 0; + } + + // Find the minimum radius among all calculated maximums. + // If any corner calculation resulted in 0, the safest radius is 0. + const safestRadius = Math.min(...allMaxRadii); + + // Ensure we don't return a negative radius if Math.min had weird input (shouldn't happen here) + return Math.max(0, safestRadius); + } + /** * Removes consecutive duplicates from the point array with tolerance * @param inputs points, tolerance and check first and last @@ -487,4 +1047,44 @@ export class Point { return dist < inputs.tolerance; } + /** + * Sorts points lexicographically (X, then Y, then Z) + * @param inputs points + * @returns sorted points + * @group sort + * @shortname sort points + * @drawable true + */ + sortPoints(inputs: Inputs.Point.PointsDto): Inputs.Base.Point3[] { + return [...inputs.points].sort((a, b) => { + if (a[0] !== b[0]) return a[0] - b[0]; + if (a[1] !== b[1]) return a[1] - b[1]; + return a[2] - b[2]; + }); + } + + /** + * Calculates the 6 vertices of a regular flat-top hexagon. + * @param center The center point [x, y, z]. + * @param radius The radius (distance from center to vertex). + * @returns An array of 6 Point3 vertices in counter-clockwise order. + */ + private getRegularHexagonVertices(center: Inputs.Base.Point3, radius: number): Inputs.Base.Point3[] { + const vertices: Inputs.Base.Point3[] = []; + const cx = center[0]; + const cy = center[1]; + const cz = center[2]; + + const angleStep = Math.PI / 3; + + for (let i = 0; i < 6; i++) { + const angle = angleStep * i; + vertices.push([ + cx + radius * Math.sin(angle), + cy + radius * Math.cos(angle), + cz // Maintain original Z + ]); + } + return vertices; + } } diff --git a/packages/dev/base/lib/api/services/polyline.test.ts b/packages/dev/base/lib/api/services/polyline.test.ts index ad4cdbc8..1e4807fb 100644 --- a/packages/dev/base/lib/api/services/polyline.test.ts +++ b/packages/dev/base/lib/api/services/polyline.test.ts @@ -5,13 +5,21 @@ import { Polyline } from "./polyline"; import { Transforms } from "./transforms"; import { Vector } from "./vector"; import * as Inputs from "../inputs"; +import { TOLERANCE, UnitTestHelper } from "../unit-test-helper"; +import { Line } from "./line"; +import { Lists } from "./lists"; describe("Polyline unit tests", () => { + + const uh = new UnitTestHelper(); + let geometryHelper: GeometryHelper; let math: MathBitByBit; let vector: Vector; let point: Point; let polyline: Polyline; + let line: Line; + let lists: Lists; let transforms: Transforms; beforeAll(() => { @@ -19,33 +27,11 @@ describe("Polyline unit tests", () => { math = new MathBitByBit(); vector = new Vector(math, geometryHelper); transforms = new Transforms(vector, math); - point = new Point(geometryHelper, transforms, vector); - polyline = new Polyline(vector, point, geometryHelper); + point = new Point(geometryHelper, transforms, vector, lists); + line = new Line(vector, point, geometryHelper); + polyline = new Polyline(vector, point, line, geometryHelper); }); - const TOLERANCE = 1e-7; - - const expectPointCloseTo = ( - received: Inputs.Base.Point3 | Inputs.Base.Vector3 | undefined, - expected: Inputs.Base.Point3 | Inputs.Base.Vector3 - ) => { - expect(received).toBeDefined(); - if (!received) return; // Guard for TS - expect(received.length).toEqual(expected.length); - expect(received[0]).toBeCloseTo(expected[0], TOLERANCE); - expect(received[1]).toBeCloseTo(expected[1], TOLERANCE); - if (expected.length > 2 && received.length > 2) { - expect(received[2]).toBeCloseTo(expected[2], TOLERANCE); - } - }; - - const expectPointsCloseTo = ( - received: Inputs.Base.Point3[] | Inputs.Base.Vector3[], - expected: Inputs.Base.Point3[] | Inputs.Base.Vector3[] - ) => { - expect(received.length).toEqual(expected.length); - received.forEach((p, i) => expectPointCloseTo(p, expected[i])); - }; it("should create polyline", () => { const p = polyline.create({ @@ -174,7 +160,7 @@ describe("Polyline unit tests", () => { const transformation = transforms.translationXYZ({ translation: translationVec }); const result = polyline.transformPolyline({ polyline: p, transformation }); const expectedPoints: Inputs.Base.Point3[] = [[10, -5, 2], [11, -4, 2]]; - expectPointsCloseTo(result.points, expectedPoints); + uh.expectPointsCloseTo(result.points, expectedPoints); }); it("should rotate all points in the polyline", () => { @@ -184,7 +170,7 @@ describe("Polyline unit tests", () => { }); const result = polyline.transformPolyline({ polyline: p, transformation }); const expectedPoints: Inputs.Base.Point3[] = [[0, 1, 0], [0, 2, 5]]; // Rotated points - expectPointsCloseTo(result.points, expectedPoints); + uh.expectPointsCloseTo(result.points, expectedPoints); }); it("should handle an empty polyline", () => { @@ -232,7 +218,7 @@ describe("Polyline unit tests", () => { [[2, 2, 0], [3, 2, 0]], ]; const result = polyline.sortSegmentsIntoPolylines({ segments }); - const sortedResult = sortPolylinesForComparison(result); + const sortedResult = uh.sortPolylinesForComparison(result); expect(sortedResult).toHaveLength(2); expect(sortedResult[0].points).toEqual([[0, 0, 0], [1, 0, 0]]); @@ -348,7 +334,7 @@ describe("Polyline unit tests", () => { [[1.05, 0, 0], [1, 1, 0]], // Does NOT connect within tolerance ]; const result = polyline.sortSegmentsIntoPolylines({ segments, tolerance }); - const sortedResult = sortPolylinesForComparison(result); + const sortedResult = uh.sortPolylinesForComparison(result); expect(sortedResult).toHaveLength(2); expect(sortedResult[0].points).toEqual([[0, 0, 0], [1, 0, 0]]); @@ -395,7 +381,7 @@ describe("Polyline unit tests", () => { [[5, 6, 5], [5, 5, 5]], ]; const result = polyline.sortSegmentsIntoPolylines({ segments }); - const sortedResult = sortPolylinesForComparison(result); + const sortedResult = uh.sortPolylinesForComparison(result); expect(sortedResult).toHaveLength(2); @@ -413,7 +399,7 @@ describe("Polyline unit tests", () => { [[1, 1, 1], [0, 0, 2]], // Seg 2: J -> C ]; const result = polyline.sortSegmentsIntoPolylines({ segments }); - const sortedResult = sortPolylinesForComparison(result); + const sortedResult = uh.sortPolylinesForComparison(result); expect(sortedResult).toHaveLength(2); expect(sortedResult).toEqual(expect.arrayContaining([ @@ -469,7 +455,7 @@ describe("Polyline unit tests", () => { [[1.21, 0, 0], [3, 0, 0]], // Seg 2 (unconnected) ]; const result = polyline.sortSegmentsIntoPolylines({ segments, tolerance }); - const sortedResult = sortPolylinesForComparison(result); + const sortedResult = uh.sortPolylinesForComparison(result); expect(sortedResult).toHaveLength(2); expect(sortedResult[0].points).toEqual([[0, 0, 0], [0.99, 0, 0], [2, 0, 0]]); // Points from original segments expect(sortedResult[0].isClosed).toBe(false); @@ -525,15 +511,335 @@ describe("Polyline unit tests", () => { }); }); - const sortPolylinesForComparison = (polylines: Inputs.Base.Polyline3[]): Inputs.Base.Polyline3[] => { - return polylines.sort((a, b) => { - const pA = a.points[0]; - const pB = b.points[0]; - if (pA[0] !== pB[0]) return pA[0] - pB[0]; - if (pA[1] !== pB[1]) return pA[1] - pB[1]; - return pA[2] - pB[2]; + describe("polylineSelfIntersection", () => { + it("should return empty array for polylines with less than 3 segments", () => { + const polyline1: Inputs.Base.Polyline3 = { points: [[0, 0, 0]], isClosed: false }; // 0 segments + const polyline2: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [1, 1, 1]], isClosed: false }; // 1 segment + const polyline3: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [1, 1, 1]], isClosed: true }; // 1 segment (closed) + const polyline4: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [1, 1, 1], [2, 0, 0]], isClosed: false }; // 2 segments + + expect(polyline.polylineSelfIntersection({ polyline: polyline1 })).toEqual([]); + expect(polyline.polylineSelfIntersection({ polyline: polyline2 })).toEqual([]); + expect(polyline.polylineSelfIntersection({ polyline: polyline3 })).toEqual([]); + expect(polyline.polylineSelfIntersection({ polyline: polyline4 })).toEqual([]); + }); + + it("should return empty array for non-intersecting simple open polyline", () => { + const pln: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], isClosed: false }; // Open Square shape + expect(polyline.polylineSelfIntersection({ polyline: pln })).toEqual([]); + }); + + it("should return empty array for non-intersecting simple closed polyline (triangle)", () => { + const pln: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [5, 0, 0], [2.5, 5, 0]], isClosed: true }; + expect(polyline.polylineSelfIntersection({ polyline: pln })).toEqual([]); + }); + + it("should return empty array for non-intersecting simple closed polyline (square)", () => { + const pln: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [5, 0, 0], [5, 5, 0], [0, 5, 0]], isClosed: true }; + expect(polyline.polylineSelfIntersection({ polyline: pln })).toEqual([]); + }); + + it("should handle intersection occurring at a non-adjacent vertex", () => { + const pln: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [2, 0, 0], [2, 2, 0], [0, 2, 0], [3, -1, 0], [1, 1, 0]], isClosed: false }; + const expected: Inputs.Base.Point3[] = [[2, 0, 0]]; + const result = polyline.polylineSelfIntersection({ polyline: pln }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should return empty array for collinear overlapping segments", () => { + const pln: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [5, 0, 0], [2, 0, 0], [8, 0, 0]], isClosed: false }; + expect(polyline.polylineSelfIntersection({ polyline: pln })).toEqual([]); + }); + + it("should find intersection for the complex user-provided polyline (open)", () => { + const pln: Inputs.Base.Polyline3 = { + points: [ + [0, 0, 0], [1, 0, 0], [1, 0, 1], [-3, 0, 0], + [-3, 0, -3], [-5, 0, 7], [-1, 0, -6] + ], + isClosed: false + }; + const expectedPoint: Inputs.Base.Point3 = [-20 / 7, 0, 1 / 28]; + const expected: Inputs.Base.Point3[] = [expectedPoint]; + const result = polyline.polylineSelfIntersection({ polyline: pln }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should handle wrap-around adjacency check for closed polylines (no intersection)", () => { + const pln: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [5, 0, 0], [5, 5, 0], [0, 5, 0]], isClosed: true }; + expect(polyline.polylineSelfIntersection({ polyline: pln })).toEqual([]); + }); + + it("should find single intersection for an open figure-eight that intersects ON segments", () => { + const pln: Inputs.Base.Polyline3 = { + points: [ + [0, 0, 0], [4, 2, 0], + [0, 4, 0], [4, -2, 0] + ], + isClosed: false + }; + const expected: Inputs.Base.Point3[] = [[2, 1, 0]]; + const result = polyline.polylineSelfIntersection({ polyline: pln }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should find two intersection points for a closed bowtie/figure-eight", () => { + const polylineStar: Inputs.Base.Polyline3 = { + points: [ + [0, 0, 0], [5, 5, 0], [10, 0, 0], [0, 4, 0], [10, 4, 0] + ], + isClosed: true + }; + const expectedStar: Inputs.Base.Point3[] = [ + [2.8571428571428568, 2.8571428571428568, 0], + [4, 4, 0], + [6, 4, 0], + [7.142857142857142, 2.857142857142857, 0], + [5, 2, 0] + ]; + const resultStar = polyline.polylineSelfIntersection({ polyline: polylineStar }); + uh.expectPointArraysCloseTo(resultStar, expectedStar); + }); + + }); + + describe("twoPolylineIntersection", () => { + + const ORIGIN: Inputs.Base.Point3 = [0, 0, 0]; + const plnLineH: Inputs.Base.Polyline3 = { points: [[-5, 0, 0], [5, 0, 0]], isClosed: false }; + const plnLineV: Inputs.Base.Polyline3 = { points: [[0, -5, 0], [0, 5, 0]], isClosed: false }; + const plnLineH_offset: Inputs.Base.Polyline3 = { points: [[-5, 2, 0], [5, 2, 0]], isClosed: false }; + const plnLineV_offset: Inputs.Base.Polyline3 = { points: [[2, -5, 0], [2, 5, 0]], isClosed: false }; + + const plnSquareClosed: Inputs.Base.Polyline3 = { + points: [[0, 0, 0], [5, 0, 0], [5, 5, 0], [0, 5, 0]], + isClosed: true + }; + const plnSquareOpen: Inputs.Base.Polyline3 = { + points: [[0, 0, 0], [5, 0, 0], [5, 5, 0], [0, 5, 0]], + isClosed: false + }; + + const plnLineAcrossSquareH: Inputs.Base.Polyline3 = { points: [[-1, 2.5, 0], [6, 2.5, 0]], isClosed: false }; + const plnLineAcrossSquareV: Inputs.Base.Polyline3 = { points: [[2.5, -1, 0], [2.5, 6, 0]], isClosed: false }; + + const plnZigzag: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [1, 1, 0], [2, 0, 0], [3, 1, 0], [4, 0, 0]], isClosed: false }; + const plnLineAcrossZigzag: Inputs.Base.Polyline3 = { points: [[0, 0.5, 0], [4, 0.5, 0]], isClosed: false }; + + const plnLineTouchingCorner: Inputs.Base.Polyline3 = { points: [[5, 5, 0], [10, 10, 0]], isClosed: false }; + + const plnShort1: Inputs.Base.Polyline3 = { points: [[100, 100, 100]], isClosed: false }; + const plnEmpty: Inputs.Base.Polyline3 = { points: [], isClosed: false }; + + describe("No Intersection", () => { + it("should return empty array when polylines are far apart", () => { + const result = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineH_offset }); + expect(result).toEqual([]); + }); + + it("should return empty array when polylines are parallel and close", () => { + const result = polyline.twoPolylineIntersection({ polyline1: plnLineV, polyline2: plnLineV_offset }); + expect(result).toEqual([]); + }); + + it("should return empty array when one polyline is empty or has one point", () => { + const result1 = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnEmpty }); + const result2 = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnShort1 }); + const result3 = polyline.twoPolylineIntersection({ polyline1: plnEmpty, polyline2: plnLineH }); + expect(result1).toEqual([]); + expect(result2).toEqual([]); + expect(result3).toEqual([]); + }); + + it("should return empty array for nested squares", () => { + const plnInnerSquare: Inputs.Base.Polyline3 = { points: [[1, 1, 0], [4, 1, 0], [4, 4, 0], [1, 4, 0]], isClosed: true }; + const result = polyline.twoPolylineIntersection({ polyline1: plnSquareClosed, polyline2: plnInnerSquare }); + expect(result).toEqual([]); + }); + + it("should return empty array for identical polylines (collinear overlap)", () => { + const plnLineH_copy: Inputs.Base.Polyline3 = { points: [[-5, 0, 0], [5, 0, 0]], isClosed: false }; + const result = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineH_copy }); + expect(result).toEqual([]); + }); + + it("should return empty array for partially overlapping collinear polylines", () => { + const plnLineH_partial: Inputs.Base.Polyline3 = { points: [[0, 0, 0], [10, 0, 0]], isClosed: false }; + const result = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineH_partial }); + expect(result).toEqual([]); + }); }); - }; + + describe("Single Intersection", () => { + it("should find one intersection point for simple crossing lines", () => { + const expected: Inputs.Base.Point3[] = [ORIGIN]; + const result = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineV }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should find one intersection point when one line crosses another offset", () => { + const expected: Inputs.Base.Point3[] = [[2, 0, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineV_offset }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should find intersection when segment endpoint lies on another segment", () => { + const plnLineV_startOnH: Inputs.Base.Polyline3 = { points: [[3, 0, 0], [3, 5, 0]], isClosed: false }; + const expected: Inputs.Base.Point3[] = [[3, 0, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineV_startOnH }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should find intersection when polylines touch at a common vertex", () => { + const expected: Inputs.Base.Point3[] = [[5, 5, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnSquareClosed, polyline2: plnLineTouchingCorner }); + uh.expectPointArraysCloseTo(result, expected); + }); + }); + + describe("Multiple Intersections", () => { + it("should find two intersection points for line crossing closed square horizontally", () => { + const expected: Inputs.Base.Point3[] = [[0, 2.5, 0], [5, 2.5, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnSquareClosed, polyline2: plnLineAcrossSquareH }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should find two intersection points for line crossing closed square vertically", () => { + const expected: Inputs.Base.Point3[] = [[2.5, 0, 0], [2.5, 5, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnSquareClosed, polyline2: plnLineAcrossSquareV }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should find only one intersection point for line crossing OPEN square horizontally", () => { + const expected: Inputs.Base.Point3[] = [[5, 2.5, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnSquareOpen, polyline2: plnLineAcrossSquareH }); + uh.expectPointArraysCloseTo(result, expected); + }); + + + it("should find four intersections for line crossing zigzag polyline", () => { + const expected: Inputs.Base.Point3[] = [[0.5, 0.5, 0], [1.5, 0.5, 0], [2.5, 0.5, 0], [3.5, 0.5, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnZigzag, polyline2: plnLineAcrossZigzag }); + uh.expectPointArraysCloseTo(result, expected); + }); + + it("should find two intersections for two overlapping closed squares", () => { + const plnSquareOffset: Inputs.Base.Polyline3 = { + points: [[2.5, 2.5, 0], [7.5, 2.5, 0], [7.5, 7.5, 0], [2.5, 7.5, 0]], + isClosed: true + }; + const expected: Inputs.Base.Point3[] = [[5, 2.5, 0], [2.5, 5, 0]]; + const result = polyline.twoPolylineIntersection({ polyline1: plnSquareClosed, polyline2: plnSquareOffset }); + uh.expectPointArraysCloseTo(result, expected); + }); + + }); + + describe("Edge Cases", () => { + it("should use custom tolerance for intersection checks", () => { + const plnLineV_Zoffset: Inputs.Base.Polyline3 = { points: [[0, -5, 1e-11], [0, 5, 1e-11]], isClosed: false }; + const resultDefault = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineV_Zoffset }); + expect(resultDefault).toEqual([]); + // With larger tolerance (e.g., 1e-3), should be considered intersecting near origin keep in mind that it's because we use + // tolerance based epsilon cube for determining if segments are skewed + const resultLoose = polyline.twoPolylineIntersection({ polyline1: plnLineH, polyline2: plnLineV_Zoffset, tolerance: 1e-3 }); + uh.expectPointArraysCloseTo(resultLoose, [ORIGIN], 1e-3); + }); + }); + }); + + describe("calculatePolylineMaxFillets", () => { + const precision = 6; + it("should return an empty array for fewer than 3 points", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [1, 1, 0]]; + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points } }; + expect(polyline.maxFilletsHalfLine(input)).toEqual([]); + }); + + it("should calculate fillet for the single corner of a 3-point open polyline", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [5, 0, 0], [5, 3, 0]]; // 90 deg corner at [5,0,0] + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: false } }; + const expectedRadii = [1.5]; + + const result = polyline.maxFilletsHalfLine(input); + uh.expectFloatArraysClose(result, expectedRadii, precision); + }); + + it("should calculate fillets for the two corners of a 4-point open polyline", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [5, 0, 0], [5, 3, 0], [2, 3, 0]]; + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: false } }; + const expectedRadii = [1.5, 1.5]; + + const result = polyline.maxFilletsHalfLine(input); + uh.expectFloatArraysClose(result, expectedRadii, precision); + }); + + it("should calculate fillets for all 3 corners of a 3-point closed polyline (triangle)", () => { + const points: Inputs.Base.Point3[] = [[0,0,0], [4,0,0], [0,3,0]]; // Right-angle triangle + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: true } }; + const expectedRadii = [2/3, 1.5, 0.75]; + + const result = polyline.maxFilletsHalfLine(input); + uh.expectFloatArraysClose(result, expectedRadii, precision); + }); + + it("should calculate fillets for all 4 corners of a 4-point closed polyline (rectangle)", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [5, 0, 0], [5, 3, 0], [0, 3, 0]]; // Rectangle + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: true } }; + const expectedRadii = [1.5, 1.5, 1.5, 1.5]; + + const result = polyline.maxFilletsHalfLine(input); + uh.expectFloatArraysClose(result, expectedRadii, precision); + }); + + it("should return 0 for corners where segments are collinear", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [5, 0, 0], [10, 0, 0], [10, 3, 0]]; // Collinear segment P0-P1-P2 + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: false } }; + const expectedRadii = [0.0, 1.5]; + + const result = polyline.maxFilletsHalfLine(input); + uh.expectFloatArraysClose(result, expectedRadii, precision); + }); + + it("should use the provided tolerance", () => { + const customTolerance = 1e-4; + const points: Inputs.Base.Point3[] = [[0, 0, 0], [customTolerance / 2, 0, 0], [1, 1, 0]]; + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: false }, tolerance: customTolerance }; + const expectedRadii = [0.0]; + + const result = polyline.maxFilletsHalfLine(input); + uh.expectFloatArraysClose(result, expectedRadii, precision); + }); + + }); + + describe("calculateSafestPolylineFillet", () => { + const precision = 6; + it("should return 0 if calculatePolylineMaxFillets returns an empty array", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [1, 1, 0]]; + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points } }; + expect(polyline.safestFilletRadius(input)).toBe(0); + }); + + it("should return the minimum of the calculated radii", () => { + const points: Inputs.Base.Point3[] = [[0,0,0], [4,0,0], [0,3,0]]; + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: true } }; + const expectedSafest = 2/3; + expect(polyline.safestFilletRadius(input)).toBeCloseTo(expectedSafest, precision); + }); + + it("should return 0 if any calculated radius is 0", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [5, 0, 0], [10, 0, 0], [10, 3, 0]]; + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: false } }; + expect(polyline.safestFilletRadius(input)).toBeCloseTo(0.0, precision); + }); + + it("should return the single radius if only one corner exists", () => { + const points: Inputs.Base.Point3[] = [[0, 0, 0], [5, 0, 0], [5, 3, 0]]; + const input: Inputs.Polyline.PolylineToleranceDto = { polyline: { points, isClosed: false } }; + expect(polyline.safestFilletRadius(input)).toBeCloseTo(1.5, precision); + }); + }); }); diff --git a/packages/dev/base/lib/api/services/polyline.ts b/packages/dev/base/lib/api/services/polyline.ts index 7e353133..d7c75cbb 100644 --- a/packages/dev/base/lib/api/services/polyline.ts +++ b/packages/dev/base/lib/api/services/polyline.ts @@ -2,6 +2,7 @@ import { GeometryHelper } from "./geometry-helper"; import * as Inputs from "../inputs"; import { Point } from "./point"; import { Vector } from "./vector"; +import { Line } from "./line"; /** * Contains various methods for polyline. Polyline in bitbybit is a simple object that has points property containing an array of points. @@ -9,7 +10,7 @@ import { Vector } from "./vector"; */ export class Polyline { - constructor(private readonly vector: Vector, private readonly point: Point, private readonly geometryHelper: GeometryHelper) { } + constructor(private readonly vector: Vector, private readonly point: Point, private readonly line: Line, private readonly geometryHelper: GeometryHelper) { } /** * Gets the length of the polyline @@ -94,6 +95,164 @@ export class Polyline { }; } + /** + * Create the lines from the polyline + * @param inputs polyline + * @returns lines + * @group convert + * @shortname polyline to lines + * @drawable true + */ + polylineToLines(inputs: Inputs.Polyline.PolylineDto): Inputs.Base.Line3[] { + const segments = this.polylineToSegments(inputs); + return segments.map((segment) => ({ + start: segment[0], + end: segment[1], + })); + } + + /** + * Create the segments from the polyline + * @param inputs polyline + * @returns segments + * @group convert + * @shortname polyline to segments + * @drawable false + */ + polylineToSegments(inputs: Inputs.Polyline.PolylineDto): Inputs.Base.Segment3[] { + const polyline = inputs.polyline; + + const segments: Inputs.Base.Segment3[] = []; + const points = polyline.points; + const numPoints = points.length; + + if (numPoints < 2) { + return segments; + } + + // Create segments between consecutive points + for (let i = 0; i < numPoints - 1; i++) { + segments.push([points[i], points[i + 1]]); + } + + // Add closing segment if the polyline is closed and has enough points + if (polyline.isClosed && numPoints >= 2) { + if (!this.point.twoPointsAlmostEqual({ point1: points[numPoints - 1], point2: points[0], tolerance: 1e-9 })) { + segments.push([points[numPoints - 1], points[0]]); + } + } + + return segments; + } + + /** + * Finds the points of self intersection of the polyline + * @param inputs points of self intersection + * @returns polyline + * @group intersections + * @shortname polyline self intersections + * @drawable true + */ + polylineSelfIntersection(inputs: Inputs.Polyline.PolylineToleranceDto): Inputs.Base.Point3[] { + const { polyline, tolerance } = inputs; + const lines = this.polylineToLines({ polyline }); + const numSegments = lines.length; + + if (numSegments < 3) { + return []; + } + + const selfIntersectionPoints: Inputs.Base.Point3[] = []; + const defaultTolerance = tolerance ?? 1e-6; + + for (let i = 0; i < numSegments; i++) { + for (let j = i + 1; j < numSegments; j++) { + let areAdjacent = (j === i + 1); + if (!areAdjacent && polyline.isClosed && i === 0 && j === numSegments - 1) { + areAdjacent = true; + } + + if (areAdjacent) { + continue; + } + + const intersection = this.line.lineLineIntersection({ + line1: lines[i], + line2: lines[j], + checkSegmentsOnly: true, + tolerance: defaultTolerance, + }); + + if (intersection) { + let foundClose = false; + for (const existingPoint of selfIntersectionPoints) { + if (this.point.twoPointsAlmostEqual({ + point1: intersection, + point2: existingPoint, + tolerance: defaultTolerance + })) { + foundClose = true; + break; + } + } + if (!foundClose) { + selfIntersectionPoints.push(intersection); + } + } + } + } + + return selfIntersectionPoints; + } + + /** + * Finds the intersection points between two polylines + * @param inputs two polylines and tolerance + * @returns points + * @group intersection + * @shortname two polyline intersection + * @drawable true + */ + twoPolylineIntersection(inputs: Inputs.Polyline.TwoPolylinesToleranceDto): Inputs.Base.Point3[] { + const { polyline1, polyline2, tolerance } = inputs; + const lines1 = this.polylineToLines({ polyline: polyline1 }); + const lines2 = this.polylineToLines({ polyline: polyline2 }); + + const intersectionPoints: Inputs.Base.Point3[] = []; + const defaultTolerance = tolerance ?? 1e-6; + + for (const seg1 of lines1) { + for (const seg2 of lines2) { + const intersection = this.line.lineLineIntersection({ + line1: seg1, + line2: seg2, + checkSegmentsOnly: true, + tolerance: defaultTolerance, + }); + + if (intersection) { + let foundClose = false; + for (const existingPoint of intersectionPoints) { + if (this.point.twoPointsAlmostEqual({ + point1: intersection, + point2: existingPoint, + tolerance: defaultTolerance + })) { + foundClose = true; + break; + } + } + + if (!foundClose) { + intersectionPoints.push(intersection); + } + } + } + } + + return intersectionPoints; + } + /** * Create the polylines from segments that are potentially connected but scrambled randomly * @param inputs segments @@ -185,11 +344,11 @@ export class Polyline { // Only consider segments not already used in *any* polyline if (!used[candidate.segmentIndex]) { const diffVector = this.vector.sub({ first: candidate.coords, second: pointToMatch }); - const distSq = this.vector.lengthSq({ vector: diffVector as Inputs.Base.Vector3}); + const distSq = this.vector.lengthSq({ vector: diffVector as Inputs.Base.Vector3 }); if (distSq < minDistanceSq) { - // Check with precise method if it's a potential best match - if (this.point.twoPointsAlmostEqual({point1: candidate.coords, point2: pointToMatch, tolerance: tolerance})){ + // Check with precise method if it's a potential best match + if (this.point.twoPointsAlmostEqual({ point1: candidate.coords, point2: pointToMatch, tolerance: tolerance })) { bestMatch = candidate; minDistanceSq = distSq; // Update min distance found } @@ -198,10 +357,10 @@ export class Polyline { } } // No need for final check here, already done inside the loop - if(bestMatch && !used[bestMatch.segmentIndex]) { // Double check used status + if (bestMatch && !used[bestMatch.segmentIndex]) { // Double check used status return bestMatch; - } - return undefined; + } + return undefined; }; @@ -269,15 +428,15 @@ export class Polyline { // Final closure check (might be redundant now, but harmless) // This catches cases like A->B, B->A which form a 2-point closed loop if (!isClosed && currentPoints.length >= 2) { - isClosed = this.point.twoPointsAlmostEqual({ point1: currentHead, point2: currentTail, tolerance: tolerance }); + isClosed = this.point.twoPointsAlmostEqual({ point1: currentHead, point2: currentTail, tolerance: tolerance }); } // Remove duplicate point for closed loops with more than 2 points if (isClosed && currentPoints.length > 2) { - // Check if the first and last points are indeed the ones needing merging - if (this.point.twoPointsAlmostEqual({ point1: currentPoints[currentPoints.length - 1], point2: currentPoints[0], tolerance: tolerance })) { - currentPoints.pop(); - } + // Check if the first and last points are indeed the ones needing merging + if (this.point.twoPointsAlmostEqual({ point1: currentPoints[currentPoints.length - 1], point2: currentPoints[0], tolerance: tolerance })) { + currentPoints.pop(); + } } // Add the completed polyline (even if it's just the starting segment) @@ -290,6 +449,65 @@ export class Polyline { return results; } + /** + * Calculates the maximum possible half-line fillet radius for each corner + * of a given polyline. For a closed polyline, it includes the corners + * connecting the last segment back to the first. + * + * The calculation uses the 'half-line' constraint, meaning the fillet's + * tangent points must lie within the first half of each segment connected + * to the corner. + * + * @param inputs Defines the polyline points, whether it's closed, and an optional tolerance. + * @returns An array containing the maximum fillet radius calculated for each corner. + * The order corresponds to corners P[1]...P[n-2] for open polylines, + * and P[1]...P[n-2], P[0], P[n-1] for closed polylines. + * Returns an empty array if the polyline has fewer than 3 points. + * @group fillet + * @shortname polyline max fillet radii + * @drawable false + */ + maxFilletsHalfLine( + inputs: Inputs.Polyline.PolylineToleranceDto + ): number[] { + return this.point.maxFilletsHalfLine({ + points: inputs.polyline.points, + checkLastWithFirst: inputs.polyline.isClosed, + tolerance: inputs.tolerance, + }); + } + + /** + * Calculates the single safest maximum fillet radius that can be applied + * uniformly to all corners of a polyline, based on the 'half-line' constraint. + * This is determined by finding the minimum of the maximum possible fillet + * radii calculated for each individual corner. + * + * @param inputs Defines the polyline points, whether it's closed, and an optional tolerance. + * @returns The smallest value from the results of calculatePolylineMaxFillets. + * Returns 0 if the polyline has fewer than 3 points or if any + * calculated maximum radius is 0. + * @group fillet + * @shortname polyline safest fillet radius + * @drawable false + */ + safestFilletRadius( + inputs: Inputs.Polyline.PolylineToleranceDto + ): number { + const allMaxRadii = this.maxFilletsHalfLine(inputs); + + if (allMaxRadii.length === 0) { + // No corners, or fewer than 3 points. No fillet possible. + return 0; + } + + // Find the minimum radius among all calculated maximums. + // If any corner calculation resulted in 0, the safest radius is 0. + const safestRadius = Math.min(...allMaxRadii); + + // Ensure we don't return a negative radius if Math.min had weird input (shouldn't happen here) + return Math.max(0, safestRadius); + } } diff --git a/packages/dev/base/lib/api/services/text.test.ts b/packages/dev/base/lib/api/services/text.test.ts index 3691d034..f026acaa 100644 --- a/packages/dev/base/lib/api/services/text.test.ts +++ b/packages/dev/base/lib/api/services/text.test.ts @@ -5,6 +5,7 @@ import { TextBitByBit } from "./text"; import { Transforms } from "./transforms"; import { Vector } from "./vector"; import * as Inputs from "../inputs"; +import { Lists } from "./lists"; describe("Text unit tests", () => { @@ -78,7 +79,8 @@ describe("Text unit tests", () => { const math = new MathBitByBit(); const vector = new Vector(math, geometryHelper); const transforms = new Transforms(vector, math); - const points = new Point(geometryHelper, transforms, vector); + const lists = new Lists(); + const points = new Point(geometryHelper, transforms, vector, lists); text = new TextBitByBit(points); }); diff --git a/packages/dev/base/lib/api/services/transforms.test.ts b/packages/dev/base/lib/api/services/transforms.test.ts index 360eb12c..1777b1b3 100644 --- a/packages/dev/base/lib/api/services/transforms.test.ts +++ b/packages/dev/base/lib/api/services/transforms.test.ts @@ -3,35 +3,17 @@ import { MathBitByBit } from "./math"; import { Transforms } from "./transforms"; import { Vector } from "./vector"; import * as Inputs from "../inputs"; +import { UnitTestHelper } from "../unit-test-helper"; describe("Transforms unit tests", () => { + + const uh = new UnitTestHelper(); + let geometryHelper: GeometryHelper; let math: MathBitByBit; let vector: Vector; let transforms: Transforms; - // Precision for floating point comparisons - const TOLERANCE = 1e-7; - - // Helper to compare two 4x4 matrices (16-element arrays) with tolerance - const expectMatrixCloseTo = (received: Inputs.Base.TransformMatrix | undefined, expected: Inputs.Base.TransformMatrix) => { - expect(received).toBeDefined(); - if (!received) return; - expect(received).toHaveLength(16); - expect(expected).toHaveLength(16); - for (let i = 0; i < 16; i++) { - expect(received[i]).toBeCloseTo(expected[i], TOLERANCE); - } - }; - - // Helper to compare arrays of matrices (like those returned by center-based operations) - const expectMatrixesCloseTo = (received: Inputs.Base.TransformMatrixes | undefined, expected: Inputs.Base.TransformMatrixes) => { - expect(received).toBeDefined(); - if (!received) return; - expect(received.length).toEqual(expected.length); - received.forEach((matrix, i) => expectMatrixCloseTo(matrix, expected[i])); - }; - beforeAll(() => { geometryHelper = new GeometryHelper(); math = new MathBitByBit(); @@ -65,7 +47,7 @@ describe("Transforms unit tests", () => { describe("identity", () => { it("should return the identity matrix", () => { const result = transforms.identity(); - expectMatrixCloseTo(result, identityMatrix); + uh.expectMatrixCloseTo(result, identityMatrix); }); }); @@ -75,7 +57,7 @@ describe("Transforms unit tests", () => { const result = transforms.translationXYZ({ translation: translationVec }); expect(result).toBeInstanceOf(Array); expect(result).toHaveLength(1); - expectMatrixCloseTo(result[0], translationMatrix(5, -10, 15)); + uh.expectMatrixCloseTo(result[0], translationMatrix(5, -10, 15)); }); }); @@ -89,8 +71,8 @@ describe("Transforms unit tests", () => { expect(result[0]).toHaveLength(1); expect(result[1]).toBeInstanceOf(Array); expect(result[1]).toHaveLength(1); - expectMatrixCloseTo(result[0][0], translationMatrix(1, 2, 3)); - expectMatrixCloseTo(result[1][0], translationMatrix(4, 5, 6)); + uh.expectMatrixCloseTo(result[0][0], translationMatrix(1, 2, 3)); + uh.expectMatrixCloseTo(result[1][0], translationMatrix(4, 5, 6)); }); it("should return an empty array for empty input", () => { @@ -104,7 +86,7 @@ describe("Transforms unit tests", () => { const scaleVec: Inputs.Base.Vector3 = [2, 0.5, -1]; const result = transforms.scaleXYZ({ scaleXyz: scaleVec }); expect(result).toHaveLength(1); - expectMatrixCloseTo(result[0], scalingMatrix(2, 0.5, -1)); + uh.expectMatrixCloseTo(result[0], scalingMatrix(2, 0.5, -1)); }); }); @@ -113,7 +95,7 @@ describe("Transforms unit tests", () => { const scaleFactor = 3.5; const result = transforms.uniformScale({ scale: scaleFactor }); expect(result).toHaveLength(1); - expectMatrixCloseTo(result[0], scalingMatrix(3.5, 3.5, 3.5)); + uh.expectMatrixCloseTo(result[0], scalingMatrix(3.5, 3.5, 3.5)); }); }); @@ -128,7 +110,7 @@ describe("Transforms unit tests", () => { scalingMatrix(scaleVec[0], scaleVec[1], scaleVec[2]), translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); }); @@ -143,7 +125,7 @@ describe("Transforms unit tests", () => { scalingMatrix(scaleFactor, scaleFactor, scaleFactor), translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); }); @@ -159,7 +141,7 @@ describe("Transforms unit tests", () => { rotationXMatrix(angleRad), translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); }); @@ -175,7 +157,7 @@ describe("Transforms unit tests", () => { rotationYMatrix(angleRad), translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); }); @@ -191,7 +173,7 @@ describe("Transforms unit tests", () => { rotationZMatrix(angleRad), translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); }); @@ -211,7 +193,7 @@ describe("Transforms unit tests", () => { expectedMiddleMatrix, translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); it("should handle non-unit axis vector by normalizing it", () => { @@ -229,7 +211,7 @@ describe("Transforms unit tests", () => { expectedMiddleMatrix, translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); }); @@ -286,7 +268,7 @@ describe("Transforms unit tests", () => { expectedMiddleMatrix, translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); it("should create translate-rotateYPR-translate matrices for pure Roll (Z rot)", () => { @@ -303,22 +285,17 @@ describe("Transforms unit tests", () => { expectedMiddleMatrix, translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2]), ]; - expectMatrixesCloseTo(result, expected); + uh.expectMatrixesCloseTo(result, expected); }); it("should handle combined rotations", () => { - // Calculating the combined matrix manually is complex. - // We'll check the structure and the translation components. const yaw = 45, pitch = 30, roll = 60; const result = transforms.rotationCenterYawPitchRoll({ center: centerPoint, yaw, pitch, roll }); expect(result).toHaveLength(3); - expectMatrixCloseTo(result[0], translationMatrix(-centerPoint[0], -centerPoint[1], -centerPoint[2])); - expectMatrixCloseTo(result[2], translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2])); - // Check the middle matrix is not identity (it should be a rotation) + uh.expectMatrixCloseTo(result[0], translationMatrix(-centerPoint[0], -centerPoint[1], -centerPoint[2])); + uh.expectMatrixCloseTo(result[2], translationMatrix(centerPoint[0], centerPoint[1], centerPoint[2])); expect(result[1]).not.toEqual(identityMatrix); - // TODO: For a more robust test, apply the resulting transform to a known point - // and verify its final position after YPR rotation. This tests the effect. }); }); diff --git a/packages/dev/base/lib/api/services/transforms.ts b/packages/dev/base/lib/api/services/transforms.ts index eee396c3..110de5f3 100644 --- a/packages/dev/base/lib/api/services/transforms.ts +++ b/packages/dev/base/lib/api/services/transforms.ts @@ -127,6 +127,24 @@ export class Transforms { return [this.scaling(inputs.scaleXyz[0], inputs.scaleXyz[1], inputs.scaleXyz[2])] as Base.TransformMatrixes; } + /** + * Creates a stretch transformation along a specific direction, relative to a center point. + * This scales points along the given direction vector while leaving points in the + * plane perpendicular to the direction (passing through the center) unchanged. + * @param inputs Defines the center, direction, and scale factor for the stretch. + * @returns Array of transformations: [Translate To Origin, Stretch, Translate Back]. + * @group scale + * @shortname stretch dir center + * @drawable false + */ + stretchDirFromCenter(inputs: Inputs.Transforms.StretchDirCenterDto): Base.TransformMatrixes { + return [ + this.translation(-inputs.center[0], -inputs.center[1], -inputs.center[2]), + this.stretchDirection(inputs.direction, inputs.scale), + this.translation(inputs.center[0], inputs.center[1], inputs.center[2]), + ] as Base.TransformMatrixes; + } + /** * Creates uniform scale transformation * @param inputs Scale Dto @@ -307,4 +325,53 @@ export class Transforms { m[15] = 1.0; return m; } + + /** + * Creates a 4x4 matrix that scales along a given direction vector. + * @param direction The direction vector (will be normalized). + * @param scale The scale factor along the direction. + * @returns A 4x4 column-major transformation matrix. + */ + private stretchDirection(direction: Base.Vector3, scale: number): Base.TransformMatrix { + const d = this.vector.normalized({ vector: direction }); + const [dx, dy, dz] = d; + + // Handle potential zero vector after normalization (if input was zero) + if (isNaN(dx) || (dx === 0 && dy === 0 && dz === 0)) { + console.warn("Stretch direction vector is zero or invalid. Returning identity matrix."); + return this.identity(); + } + + const s = scale; + const sMinus1 = s - 1.0; + + // Calculate elements of the 3x3 directional scaling part + const m11 = 1.0 + sMinus1 * dx * dx; + const m12 = sMinus1 * dx * dy; + const m13 = sMinus1 * dx * dz; + // m14 = 0 + + const m21 = sMinus1 * dy * dx; + const m22 = 1.0 + sMinus1 * dy * dy; + const m23 = sMinus1 * dy * dz; + // m24 = 0 + + const m31 = sMinus1 * dz * dx; + const m32 = sMinus1 * dz * dy; + const m33 = 1.0 + sMinus1 * dz * dz; + // m34 = 0 + + // m41, m42, m43 = 0, m44 = 1 + + // Assemble the 4x4 matrix in COLUMN-MAJOR order + const m: Base.TransformMatrix = [ + m11, m21, m31, 0.0, // Column 1 + m12, m22, m32, 0.0, // Column 2 + m13, m23, m33, 0.0, // Column 3 + 0.0, 0.0, 0.0, 1.0 // Column 4 + ]; + + return m; + } + } diff --git a/packages/dev/base/lib/api/services/vector.ts b/packages/dev/base/lib/api/services/vector.ts index 1c4434d0..68382ae1 100644 --- a/packages/dev/base/lib/api/services/vector.ts +++ b/packages/dev/base/lib/api/services/vector.ts @@ -358,6 +358,10 @@ export class Vector { * @drawable false */ normalized(inputs: Inputs.Vector.VectorDto): number[] { + const len = this.length({vector: inputs.vector as Inputs.Base.Vector3}); + if (len <= 1e-8) { + return undefined; + } return this.div({ scalar: this.norm(inputs), vector: inputs.vector }); } @@ -529,4 +533,16 @@ export class Vector { const v = inputs.vector; return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; } + + /** + * Computes the length of the vector + * @param inputs Vector to compute the length + * @returns Number that is length of the vector + * @group base + * @shortname length + * @drawable false + */ + length(inputs: Inputs.Vector.Vector3Dto): number { + return Math.sqrt(this.lengthSq(inputs)); + } } diff --git a/packages/dev/base/lib/api/unit-test-helper.ts b/packages/dev/base/lib/api/unit-test-helper.ts new file mode 100644 index 00000000..36c7ad56 --- /dev/null +++ b/packages/dev/base/lib/api/unit-test-helper.ts @@ -0,0 +1,164 @@ +import * as Inputs from "./inputs"; +import { GeometryHelper, MathBitByBit, Vector } from "./services"; + +export const TOLERANCE = 1e-7; + +export class UnitTestHelper { + vector: Vector; + constructor() { + const math = new MathBitByBit(); + const geometryHelper = new GeometryHelper(); + this.vector = new Vector(math, geometryHelper); + } + + expectPointCloseTo( + received: Inputs.Base.Point3 | Inputs.Base.Vector3 | undefined, + expected: Inputs.Base.Point3 | Inputs.Base.Vector3 + ) { + expect(received).toBeDefined(); + if (!received) return; + expect(received.length).toEqual(expected.length); + expect(received[0]).toBeCloseTo(expected[0], TOLERANCE); + expect(received[1]).toBeCloseTo(expected[1], TOLERANCE); + if (expected.length > 2 && received.length > 2) { + expect(received[2]).toBeCloseTo(expected[2], TOLERANCE); + } + } + + expectPointsCloseTo( + received: Inputs.Base.Point3[] | Inputs.Base.Vector3[], + expected: Inputs.Base.Point3[] | Inputs.Base.Vector3[] + ) { + expect(received.length).toEqual(expected.length); + received.forEach((p, i) => this.expectPointCloseTo(p, expected[i])); + } + + expectLineCloseTo( + received: Inputs.Base.Line3 | undefined, + expected: Inputs.Base.Line3 + ) { + expect(received).toBeDefined(); + if (!received) return; + this.expectPointCloseTo(received.start, expected.start); + this.expectPointCloseTo(received.end, expected.end); + } + + expectLinesCloseTo( + received: Inputs.Base.Line3[], + expected: Inputs.Base.Line3[] + ) { + expect(received.length).toEqual(expected.length); + received.forEach((l, i) => this.expectLineCloseTo(l, expected[i])); + } + + expectSegmentCloseTo( + received: Inputs.Base.Segment3 | undefined, + expected: Inputs.Base.Segment3, + precision = TOLERANCE + ) { + expect(received).toBeDefined(); + if (!received) return; + expect(received).toHaveLength(2); + const order1Matches = Math.abs(this.vector.dist({ first: received[0], second: expected[0] })) < precision && + Math.abs(this.vector.dist({ first: received[1], second: expected[1] })) < precision; + const order2Matches = Math.abs(this.vector.dist({ first: received[0], second: expected[1] })) < precision && + Math.abs(this.vector.dist({ first: received[1], second: expected[0] })) < precision; + expect(order1Matches || order2Matches).toBe(true); + } + + expectPlaneCloseTo( + received: Inputs.Base.TrianglePlane3 | undefined, + expected: Inputs.Base.TrianglePlane3, + precision = TOLERANCE + ) { + expect(received).toBeDefined(); + if (!received) return; + const normalDir1 = this.vector.sub({ first: received.normal, second: expected.normal }); + const normalDir2 = this.vector.add({ first: received.normal, second: expected.normal }); + const dir1Match = this.vector.lengthSq({ vector: normalDir1 as Inputs.Base.Vector3 }) < precision * precision; + const dir2Match = this.vector.lengthSq({ vector: normalDir2 as Inputs.Base.Vector3 }) < precision * precision; + expect(dir1Match || dir2Match).toBe(true); + expect(received.d).toBeCloseTo(dir1Match ? expected.d : -expected.d, precision); + } + + expectMatrixCloseTo(received: Inputs.Base.TransformMatrix | undefined, expected: Inputs.Base.TransformMatrix) { + expect(received).toBeDefined(); + if (!received) return; + expect(received).toHaveLength(16); + expect(expected).toHaveLength(16); + for (let i = 0; i < 16; i++) { + expect(received[i]).toBeCloseTo(expected[i], TOLERANCE); + } + } + + expectMatrixesCloseTo(received: Inputs.Base.TransformMatrixes | undefined, expected: Inputs.Base.TransformMatrixes) { + expect(received).toBeDefined(); + if (!received) return; + expect(received.length).toEqual(expected.length); + received.forEach((matrix, i) => this.expectMatrixCloseTo(matrix, expected[i])); + } + + /** Helper to compare two arrays of points for near-equality, ignoring order */ + expectPointArraysCloseTo( + actual: Inputs.Base.Point3[] | undefined, + expected: Inputs.Base.Point3[], + tolerance = 1e-6 + ) { + // Use expectPointsClose helper for individual point comparison if needed + const expectPointsClose = (act: Inputs.Base.Point3 | undefined, exp: Inputs.Base.Point3 | undefined, tol = 1e-6) => { + const precision = Math.max(0, Math.ceil(-Math.log10(tol)) - 1); + if (exp === undefined) { + expect(act).toBeUndefined(); + } else { + expect(act).toBeDefined(); + if (act) { + expect(act[0]).toBeCloseTo(exp[0], precision); + expect(act[1]).toBeCloseTo(exp[1], precision); + expect(act[2]).toBeCloseTo(exp[2], precision); + } + } + }; + + expect(actual).toBeDefined(); + + // Proceed only if actual is defined + if (actual) { + expect(actual.length).toEqual(expected.length); + + // Sort both arrays to compare independent of order + const sortedActual = this.sortPoints(actual); + const sortedExpected = this.sortPoints(expected); + + for (let i = 0; i < sortedExpected.length; i++) { + expectPointsClose(sortedActual[i], sortedExpected[i], tolerance); + } + } + } + + sortPoints(points: Inputs.Base.Point3[]): Inputs.Base.Point3[] { + return [...points].sort((a, b) => { + if (a[0] !== b[0]) return a[0] - b[0]; + if (a[1] !== b[1]) return a[1] - b[1]; + return a[2] - b[2]; + }); + } + + sortPolylinesForComparison(polylines: Inputs.Base.Polyline3[]): Inputs.Base.Polyline3[] { + return polylines.sort((a, b) => { + const pA = a.points[0]; + const pB = b.points[0]; + if (pA[0] !== pB[0]) return pA[0] - pB[0]; + if (pA[1] !== pB[1]) return pA[1] - pB[1]; + return pA[2] - pB[2]; + }); + } + + expectFloatArraysClose(actual: number[], expected: number[], precision: number) { + expect(actual.length).toBe(expected.length); + actual.forEach((val, index) => { + expect(val).toBeCloseTo(expected[index], precision); + }); + }; + + +} \ No newline at end of file diff --git a/packages/dev/base/package-lock.json b/packages/dev/base/package-lock.json index abb18c04..8943f228 100644 --- a/packages/dev/base/package-lock.json +++ b/packages/dev/base/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitbybit-dev/base", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/base", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "devDependencies": { "@babel/core": "7.16.0", diff --git a/packages/dev/base/package.json b/packages/dev/base/package.json index 6c82d9f1..585d3b88 100644 --- a/packages/dev/base/package.json +++ b/packages/dev/base/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/base", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers Base CAD Library to Program Geometry", "main": "index.js", "repository": { diff --git a/packages/dev/core/lib/api/bitbybit/verb/curve.ts b/packages/dev/core/lib/api/bitbybit/verb/curve.ts index 0fce66a4..c2b163f0 100644 --- a/packages/dev/core/lib/api/bitbybit/verb/curve.ts +++ b/packages/dev/core/lib/api/bitbybit/verb/curve.ts @@ -42,6 +42,46 @@ export class VerbCurve { return this.context.verb.geom.NurbsCurve.byPoints(inputs.points, inputs.degree); } + /** + * Converts lines to NURBS curves + * Returns array of the verbnurbs Line objects + * @param inputs Lines to be transformed to curves + * @returns Verb nurbs curves + */ + convertLinesToNurbsCurves(inputs: Inputs.Verb.LinesDto): any[] { + return inputs.lines.map(line => new this.context.verb.geom.Line(line.start, line.end)); + } + + /** + * Converts line to NURBS curve + * Returns the verbnurbs Line object + * @param inputs Line to be transformed to curve + * @returns Verb nurbs curves + */ + convertLineToNurbsCurve(inputs: Inputs.Verb.LineDto): any { + return new this.context.verb.geom.Line(inputs.line.start, inputs.line.end); + } + + /** + * Converts a polyline to a NURBS curve + * Returns the verbnurbs NurbsCurve object + * @param inputs Polyline to be transformed to curve + * @returns Verb nurbs curve + */ + convertPolylineToNurbsCurve(inputs: Inputs.Verb.PolylineDto): any { + return this.context.verb.geom.NurbsCurve.byPoints(inputs.polyline.points, 1); + } + + /** + * Converts a polylines to a NURBS curves + * Returns the verbnurbs NurbsCurve objects + * @param inputs Polylines to be transformed to curves + * @returns Verb nurbs curves + */ + convertPolylinesToNurbsCurves(inputs: Inputs.Verb.PolylinesDto): any[] { + return inputs.polylines.map(polyline => this.convertPolylineToNurbsCurve({ polyline })); + } + /** * Creates a Bezier Nurbs curve by providing control points and weights * @param inputs Control points diff --git a/packages/dev/core/lib/api/inputs/verb-inputs.ts b/packages/dev/core/lib/api/inputs/verb-inputs.ts index 0d14d7c2..7b5fc9c7 100644 --- a/packages/dev/core/lib/api/inputs/verb-inputs.ts +++ b/packages/dev/core/lib/api/inputs/verb-inputs.ts @@ -14,6 +14,42 @@ export namespace Verb { */ curve: any; } + export class LineDto { + constructor(line?: Base.Line3) { + if (line !== undefined) { this.line = line; } + } + /** + * Basic line + */ + line: Base.Line3; + } + export class LinesDto { + constructor(lines?: Base.Line3[]) { + if (lines !== undefined) { this.lines = lines; } + } + /** + * Basic lines + */ + lines: Base.Line3[]; + } + export class PolylineDto { + constructor(polyline?: Base.Polyline3) { + if (polyline !== undefined) { this.polyline = polyline; } + } + /** + * Basic polyline + */ + polyline: Base.Polyline3; + } + export class PolylinesDto { + constructor(polylines?: Base.Polyline3[]) { + if (polylines !== undefined) { this.polylines = polylines; } + } + /** + * Basic polyline + */ + polylines: Base.Polyline3[]; + } export class CurvesDto { constructor(curves?: any[]) { if (curves !== undefined) { this.curves = curves; } diff --git a/packages/dev/core/package-lock.json b/packages/dev/core/package-lock.json index 3e428f20..c91a3ca4 100644 --- a/packages/dev/core/package-lock.json +++ b/packages/dev/core/package-lock.json @@ -1,18 +1,18 @@ { "name": "@bitbybit-dev/core", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/core", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.2", - "@bitbybit-dev/jscad-worker": "0.20.2", - "@bitbybit-dev/manifold-worker": "0.20.2", - "@bitbybit-dev/occt-worker": "0.20.2", + "@bitbybit-dev/base": "0.20.3", + "@bitbybit-dev/jscad-worker": "0.20.3", + "@bitbybit-dev/manifold-worker": "0.20.3", + "@bitbybit-dev/occt-worker": "0.20.3", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" @@ -1707,16 +1707,16 @@ "dev": true }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "dependencies": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -1725,45 +1725,45 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.2.tgz", - "integrity": "sha512-bTnlO3yqvTNFqAx3dGnhrX/O7HPNuXRE5vn68ogK7belWEI8r26XaN5kJEzcN8k9maxAXJQRR04xJQPkbfGTaA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.3.tgz", + "integrity": "sha512-V1B7FPvpyDbDjmlunaaCSUo2kkMT164YaDoQMRNGjxknLI+lzymz13W7J99VZrlekJY2RSaMDaYx+9k0ELXbtw==", "dependencies": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "dependencies": { "manifold-3d": "3.0.0" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.2.tgz", - "integrity": "sha512-+Zid8ALBXGTSLn6DlhU8abHduu5HlUpFZ5+RoIshXi4z5xQJbVhEWckDn1PF2Uq9y4qnwHTMQK+etF40Aei2MQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.3.tgz", + "integrity": "sha512-EWN8k6yXK1XWXK/Km4Jx3xB9H3joEi1XJo48OU8CQHRMvplpMt0P5LmqiskyT9Y5Gm16YLOVm/8AX6f4Vra9vQ==", "dependencies": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "dependencies": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.2.tgz", - "integrity": "sha512-q6h/PbBAKnp01PshmhGE14x2b7YnW02YvnUrdGkU6FVYErxoQwwASiN10UKgeBbkD+jbc099kpFzJtvamPnMRw==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.3.tgz", + "integrity": "sha512-2fDVkW42TPzrAswTCX9jWU96L2TkTGssIb82iX+ORnv8vpUb/P7Ih5vMsUUkHLZmFyft61dTvPDSjN1bqPAgQw==", "dependencies": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" } }, @@ -8379,16 +8379,16 @@ "dev": true }, "@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "requires": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -8397,45 +8397,45 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.2.tgz", - "integrity": "sha512-bTnlO3yqvTNFqAx3dGnhrX/O7HPNuXRE5vn68ogK7belWEI8r26XaN5kJEzcN8k9maxAXJQRR04xJQPkbfGTaA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.3.tgz", + "integrity": "sha512-V1B7FPvpyDbDjmlunaaCSUo2kkMT164YaDoQMRNGjxknLI+lzymz13W7J99VZrlekJY2RSaMDaYx+9k0ELXbtw==", "requires": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "requires": { "manifold-3d": "3.0.0" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.2.tgz", - "integrity": "sha512-+Zid8ALBXGTSLn6DlhU8abHduu5HlUpFZ5+RoIshXi4z5xQJbVhEWckDn1PF2Uq9y4qnwHTMQK+etF40Aei2MQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.3.tgz", + "integrity": "sha512-EWN8k6yXK1XWXK/Km4Jx3xB9H3joEi1XJo48OU8CQHRMvplpMt0P5LmqiskyT9Y5Gm16YLOVm/8AX6f4Vra9vQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "requires": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.2.tgz", - "integrity": "sha512-q6h/PbBAKnp01PshmhGE14x2b7YnW02YvnUrdGkU6FVYErxoQwwASiN10UKgeBbkD+jbc099kpFzJtvamPnMRw==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.3.tgz", + "integrity": "sha512-2fDVkW42TPzrAswTCX9jWU96L2TkTGssIb82iX+ORnv8vpUb/P7Ih5vMsUUkHLZmFyft61dTvPDSjN1bqPAgQw==", "requires": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" } }, diff --git a/packages/dev/core/package.json b/packages/dev/core/package.json index 046bee9a..50e17fb3 100644 --- a/packages/dev/core/package.json +++ b/packages/dev/core/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/core", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers Core CAD API to Program Geometry", "main": "index.js", "repository": { @@ -54,10 +54,10 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "@bitbybit-dev/base": "0.20.2", - "@bitbybit-dev/occt-worker": "0.20.2", - "@bitbybit-dev/manifold-worker": "0.20.2", - "@bitbybit-dev/jscad-worker": "0.20.2", + "@bitbybit-dev/base": "0.20.3", + "@bitbybit-dev/occt-worker": "0.20.3", + "@bitbybit-dev/manifold-worker": "0.20.3", + "@bitbybit-dev/jscad-worker": "0.20.3", "jsonpath-plus": "10.1.0", "verb-nurbs-web": "2.1.3", "rxjs": "7.5.5" diff --git a/packages/dev/jscad-worker/lib/api/jscad.ts b/packages/dev/jscad-worker/lib/api/jscad.ts index 23856107..7fa55d1c 100644 --- a/packages/dev/jscad-worker/lib/api/jscad.ts +++ b/packages/dev/jscad-worker/lib/api/jscad.ts @@ -40,6 +40,18 @@ export class JSCAD { this.colors = new JSCADColors(jscadWorkerManager); } + /** + * Converts the Jscad mesh to polygon points representing triangles of the mesh. + * @param inputs Jscad mesh + * @returns polygon points + * @group conversions + * @shortname to polygon points + * @drawable false + */ + async toPolygonPoints(inputs: Inputs.JSCAD.MeshDto): Promise { + return this.jscadWorkerManager.genericCallToWorkerPromise("toPolygonPoints", inputs); + } + /** * Transforms the Jscad solid meshes with a given list of transformations. * @param inputs Solids with the transformation matrixes diff --git a/packages/dev/jscad-worker/package-lock.json b/packages/dev/jscad-worker/package-lock.json index 3cf819e2..1b775be6 100644 --- a/packages/dev/jscad-worker/package-lock.json +++ b/packages/dev/jscad-worker/package-lock.json @@ -1,15 +1,15 @@ { "name": "@bitbybit-dev/jscad-worker", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/jscad-worker", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" }, "devDependencies": { @@ -1702,16 +1702,16 @@ "dev": true }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "dependencies": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -8236,16 +8236,16 @@ "dev": true }, "@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "requires": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", diff --git a/packages/dev/jscad-worker/package.json b/packages/dev/jscad-worker/package.json index 15d2b2c5..289f309f 100644 --- a/packages/dev/jscad-worker/package.json +++ b/packages/dev/jscad-worker/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/jscad-worker", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers JSCAD Based CAD Library to Program Geometry Via WebWorker", "main": "index.js", "repository": { @@ -60,7 +60,7 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" }, "devDependencies": { diff --git a/packages/dev/jscad/lib/api/inputs/jscad-inputs.ts b/packages/dev/jscad/lib/api/inputs/jscad-inputs.ts index bb374d27..3345a56d 100644 --- a/packages/dev/jscad/lib/api/inputs/jscad-inputs.ts +++ b/packages/dev/jscad/lib/api/inputs/jscad-inputs.ts @@ -54,6 +54,25 @@ export namespace JSCAD { */ right = "right", } + export class MeshDto { + constructor(mesh?: JSCADEntity) { + if (mesh !== undefined) { this.mesh = mesh; } + } + /** + * Solid Jscad mesh + */ + mesh: JSCADEntity; + } + + export class MeshesDto { + constructor(meshes?: JSCADEntity[]) { + if (meshes !== undefined) { this.meshes = meshes; } + } + /** + * Solid Jscad mesh + */ + meshes: JSCADEntity[]; + } export class DrawSolidMeshDto { /** * Provide options without default values diff --git a/packages/dev/jscad/lib/api/jscad-service.ts b/packages/dev/jscad/lib/api/jscad-service.ts index 5bf21f21..2ab9345e 100644 --- a/packages/dev/jscad/lib/api/jscad-service.ts +++ b/packages/dev/jscad/lib/api/jscad-service.ts @@ -1,4 +1,4 @@ -import { GeometryHelper } from "@bitbybit-dev/base"; +import { GeometryHelper, Lists, Point, Transforms, Vector } from "@bitbybit-dev/base"; import { MathBitByBit } from "@bitbybit-dev/base"; import { JSCADExpansions } from "./services/jscad-expansions"; import { JSCADBooleans } from "./services/jscad-booleans"; @@ -8,6 +8,7 @@ import { JSCADPolygon } from "./services/jscad-polygon"; import { JSCADShapes } from "./services/jscad-shapes"; import { JSCADText } from "./services/jscad-text"; import * as Inputs from "./inputs/jscad-inputs"; +import { Base } from "./inputs/base-inputs"; import { JSCADHulls } from "./services/jscad-hulls"; import { JSCADColors } from "./services/jscad-colors"; import * as JSCAD from "@jscad/modeling"; @@ -27,10 +28,15 @@ export class Jscad { public shapes: JSCADShapes; public text: JSCADText; public colors: JSCADColors; + private point: Point; constructor(jscad: typeof JSCAD) { const geometryHelper = new GeometryHelper(); const math = new MathBitByBit(); + const vector = new Vector(math, geometryHelper); + const transforms = new Transforms(vector, math); + const lists = new Lists(); + this.point = new Point(geometryHelper, transforms, vector, lists); this.booleans = new JSCADBooleans(jscad); this.expansions = new JSCADExpansions(jscad); this.extrusions = new JSCADExtrusions(jscad, geometryHelper, math); @@ -43,13 +49,89 @@ export class Jscad { this.jscad = jscad; } - shapesToMeshes(inputs: Inputs.JSCAD.DrawSolidMeshesDto): { positions: number[], normals: number[], indices: number[], transforms: [] }[] { + toPolygonPoints(inputs: Inputs.JSCAD.MeshDto): Base.Mesh3 { + + const meshData = this.shapeToMesh({ mesh: inputs.mesh }); + + if (!meshData || !meshData.positions || !meshData.indices) { + throw new Error("Invalid input: 'data', 'data.positions', and 'data.indices' must be provided."); + } + + const { positions, indices } = meshData; + + if (positions.length % 3 !== 0) { + throw new Error(`Invalid input: 'positions' array length (${positions.length}) must be a multiple of 3.`); + } + + if (indices.length % 3 !== 0) { + throw new Error(`Invalid input: 'indices' array length (${indices.length}) must be a multiple of 3.`); + } + + if (positions.length === 0) { + return []; + } + if (indices.length === 0) { + return []; + } + + + const polygons: Base.Mesh3 = []; + const numVertices = positions.length / 3; + + // --- Triangle Reconstruction --- + for (let i = 0; i < indices.length; i += 3) { + const index1 = indices[i]; + const index2 = indices[i + 1]; + const index3 = indices[i + 2]; + + if (index1 >= numVertices || index2 >= numVertices || index3 >= numVertices || + index1 < 0 || index2 < 0 || index3 < 0) { + console.error(`Invalid vertex index found in 'indices' array at triangle starting at index ${i}. Max vertex index is ${numVertices - 1}. Indices: ${index1}, ${index2}, ${index3}. Skipping triangle.`); + continue; + } + + const offset1 = index1 * 3; + const offset2 = index2 * 3; + const offset3 = index3 * 3; + + const point1: Base.Point3 = [positions[offset1], positions[offset1 + 1], positions[offset1 + 2]]; + const point2: Base.Point3 = [positions[offset2], positions[offset2 + 1], positions[offset2 + 2]]; + const point3: Base.Point3 = [positions[offset3], positions[offset3 + 1], positions[offset3 + 2]]; + + // We must bake the transformations as JSCAD uses those extensively + const transformation = inputs.mesh.transforms; + let transformedPoints = [point1, point2, point3]; + if (this.getArrayDepth(transformation) === 2) { + transformation.forEach(transform => { + transformedPoints = this.point.transformPoints({ points: transformedPoints, transformation: [transform] }); + }); + } + else if (this.getArrayDepth(transformation) === 3) { + transformation.forEach(transforms => { + transforms.forEach(mat => { + transformedPoints = this.point.transformPoints({ points: transformedPoints, transformation: [mat] }); + }); + }); + } + else { + transformedPoints = this.point.transformPoints({ points: transformedPoints, transformation: [transformation] }); + } + + const triangle: Base.Triangle3 = transformedPoints as Base.Triangle3; + + polygons.push(triangle); + } + + return polygons; + } + + shapesToMeshes(inputs: Inputs.JSCAD.MeshesDto): { positions: number[], normals: number[], indices: number[], transforms: [] }[] { return inputs.meshes.map(mesh => { return this.shapeToMesh({ ...inputs, mesh }); }); } - shapeToMesh(inputs: Inputs.JSCAD.DrawSolidMeshDto): { positions: number[], normals: number[], indices: number[], transforms: [] } { + shapeToMesh(inputs: Inputs.JSCAD.MeshDto): { positions: number[], normals: number[], indices: number[], transforms: [] } { let polygons = []; if (inputs.mesh.toPolygons) { diff --git a/packages/dev/jscad/package-lock.json b/packages/dev/jscad/package-lock.json index 1e58a616..fedd581a 100644 --- a/packages/dev/jscad/package-lock.json +++ b/packages/dev/jscad/package-lock.json @@ -1,15 +1,15 @@ { "name": "@bitbybit-dev/jscad", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/jscad", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -1706,9 +1706,9 @@ "dev": true }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", @@ -8214,9 +8214,9 @@ "dev": true }, "@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "@cspotcode/source-map-support": { "version": "0.8.1", diff --git a/packages/dev/jscad/package.json b/packages/dev/jscad/package.json index 373db8f8..a38e0484 100644 --- a/packages/dev/jscad/package.json +++ b/packages/dev/jscad/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/jscad", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers JSCAD based CAD Library to Program Geometry", "main": "index.js", "repository": { @@ -58,7 +58,7 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/io-utils": "2.0.28", "@jscad/modeling": "2.12.3", "@jscad/stl-serializer": "2.1.18", diff --git a/packages/dev/manifold-worker/lib/api/manifold-bitbybit.ts b/packages/dev/manifold-worker/lib/api/manifold-bitbybit.ts index d2b894a0..b99fb082 100644 --- a/packages/dev/manifold-worker/lib/api/manifold-bitbybit.ts +++ b/packages/dev/manifold-worker/lib/api/manifold-bitbybit.ts @@ -45,6 +45,18 @@ export class ManifoldBitByBit { return this.manifoldWorkerManager.genericCallToWorkerPromise("decomposeManifoldOrCrossSection", inputs); } + /** + * Turns manifold shape into a collection of polygon points representing the mesh. + * @param inputs Manifold shape + * @returns polygon points + * @group decompose + * @shortname to polygon points + * @drawable false + */ + async toPolygonPoints(inputs: Inputs.Manifold.ManifoldDto): Promise { + return this.manifoldWorkerManager.genericCallToWorkerPromise("toPolygonPoints", inputs); + } + /** * Decomposes manifold or cross section shape into a mesh or simple polygons * @param inputs Manifold shapes or cross sections diff --git a/packages/dev/manifold-worker/package-lock.json b/packages/dev/manifold-worker/package-lock.json index 0c87086a..a046b6d8 100644 --- a/packages/dev/manifold-worker/package-lock.json +++ b/packages/dev/manifold-worker/package-lock.json @@ -1,15 +1,15 @@ { "name": "@bitbybit-dev/manifold-worker", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/manifold-worker", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" }, "devDependencies": { @@ -1702,9 +1702,9 @@ "dev": true }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "dependencies": { "manifold-3d": "3.0.0" } @@ -8169,9 +8169,9 @@ "dev": true }, "@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "requires": { "manifold-3d": "3.0.0" } diff --git a/packages/dev/manifold-worker/package.json b/packages/dev/manifold-worker/package.json index 71545227..65c3b87f 100644 --- a/packages/dev/manifold-worker/package.json +++ b/packages/dev/manifold-worker/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/manifold-worker", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers Manifold Based CAD Library to Program Geometry Via WebWorker", "main": "index.js", "repository": { @@ -60,7 +60,7 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" }, "devDependencies": { diff --git a/packages/dev/manifold/lib/api/manifold-service.ts b/packages/dev/manifold/lib/api/manifold-service.ts index ea49c3fa..528f03cd 100644 --- a/packages/dev/manifold/lib/api/manifold-service.ts +++ b/packages/dev/manifold/lib/api/manifold-service.ts @@ -7,7 +7,7 @@ import { Mesh } from "./services/mesh/mesh"; // Worker make an instance of this class itself export class ManifoldService { plugins: any; - + public crossSection: CrossSection; public manifold: Manifold; mesh: Mesh; @@ -18,6 +18,65 @@ export class ManifoldService { this.mesh = new Mesh(wasm); } + toPolygonPoints(inputs: Inputs.Manifold.ManifoldDto): Inputs.Base.Mesh3 { + // Ensure the manifold is decomposed to access the mesh data. + // The getMesh() method provides the necessary structure. + if ((inputs.manifold as Manifold3D.Manifold).getMesh) { + + const mesh = inputs.manifold.getMesh(); + + if (!mesh || !mesh.vertProperties || !mesh.triVerts || mesh.vertProperties.length === 0 || mesh.triVerts.length === 0) { + console.warn("Manifold does not contain valid mesh data or is empty."); + return []; + } + + const vertProperties = mesh.vertProperties; // Float32Array [x1, y1, z1, [prop4...], x2, y2, z2, [prop4...]...] + const triVerts = mesh.triVerts; // Uint32Array [idx1, idx2, idx3, idx4, idx5, idx6, ...] + const numProp = mesh.numProp; // Number of properties per vertex + + // We need at least x, y, z data. + if (numProp < 3) { + throw new Error(`Cannot convert to PolygonPoints: Expected numProp >= 3 (for x, y, z), but found ${numProp}.`); + } + + if (triVerts.length % 3 !== 0) { + throw new Error(`Mesh data corruption: triVerts length (${triVerts.length}) is not a multiple of 3.`); + } + + const polygons: Inputs.Base.Mesh3 = []; + const numVertices = vertProperties.length / numProp; + + for (let i = 0; i < triVerts.length; i += 3) { + const index1 = triVerts[i]; + const index2 = triVerts[i + 1]; + const index3 = triVerts[i + 2]; + + if (index1 >= numVertices || index2 >= numVertices || index3 >= numVertices) { + console.error(`Invalid vertex index found in triVerts at offset ${i}. Max index should be ${numVertices - 1}. Indices: ${index1}, ${index2}, ${index3}`); + continue; // Skip this triangle + } + + // Calculate the starting position of each vertex's data in vertProperties. + // This calculation works regardless of numProp. + const vert1Offset = index1 * numProp; + const vert2Offset = index2 * numProp; + const vert3Offset = index3 * numProp; + + // Extract only the first 3 properties (x, y, z) starting from the offset for each vertex. + const point1: Inputs.Base.Point3 = [vertProperties[vert1Offset], vertProperties[vert1Offset + 1], vertProperties[vert1Offset + 2]]; + const point2: Inputs.Base.Point3 = [vertProperties[vert2Offset], vertProperties[vert2Offset + 1], vertProperties[vert2Offset + 2]]; + const point3: Inputs.Base.Point3 = [vertProperties[vert3Offset], vertProperties[vert3Offset + 1], vertProperties[vert3Offset + 2]]; + + const triangle: Inputs.Base.Triangle3 = [point1, point2, point3]; + polygons.push(triangle); + } + + return polygons; + } else { + return undefined; + } + } + decomposeManifoldOrCrossSection(inputs: Inputs.Manifold.DecomposeManifoldOrCrossSectionDto): Manifold3D.Mesh | Manifold3D.SimplePolygon[] { if ((inputs.manifoldOrCrossSection as Manifold3D.Manifold).getMesh) { return (inputs.manifoldOrCrossSection as Manifold3D.Manifold).getMesh(inputs.normalIdx); diff --git a/packages/dev/manifold/package-lock.json b/packages/dev/manifold/package-lock.json index e0e1a2eb..5992c8dd 100644 --- a/packages/dev/manifold/package-lock.json +++ b/packages/dev/manifold/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitbybit-dev/manifold", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/manifold", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { "manifold-3d": "3.0.0" diff --git a/packages/dev/manifold/package.json b/packages/dev/manifold/package.json index d49a125c..350bdf28 100644 --- a/packages/dev/manifold/package.json +++ b/packages/dev/manifold/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/manifold", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers Manifold based CAD Library to Program Geometry", "main": "index.js", "repository": { diff --git a/packages/dev/occt-worker/lib/api/occt/booleans.ts b/packages/dev/occt-worker/lib/api/occt/booleans.ts index 1b23d710..d958b1d0 100644 --- a/packages/dev/occt-worker/lib/api/occt/booleans.ts +++ b/packages/dev/occt-worker/lib/api/occt/booleans.ts @@ -43,4 +43,56 @@ export class OCCTBooleans { intersection(inputs: Inputs.OCCT.IntersectionDto): Promise { return this.occWorkerManager.genericCallToWorkerPromise("booleans.intersection", inputs); } + + /** + * Does mesh mesh intersection operation between two shapes - both shapes can have their own meshing precision. + * This algorithm intersects the meshes and returns the wires of the intersection, which are polylines or polygons. + * @param inputs Two shapes to intersect + * @returns Wires where shapes intersect + * @group mesh based + * @shortname mesh mesh intersection as wires + * @drawable true + */ + meshMeshIntersectionWires(inputs: Inputs.OCCT.MeshMeshIntersectionTwoShapesDto): Promise{ + return this.occWorkerManager.genericCallToWorkerPromise("booleans.meshMeshIntersectionWires", inputs); + } + + /** + * Does mesh mesh intersection operation between two shapes - both shapes can have their own meshing precision. + * This algorithm intersects the meshes and returns the points of the intersection, which are polylines or polygons. + * @param inputs Two shapes to intersect + * @returns Points where shapes intersect + * @group mesh based + * @shortname mesh mesh intersection as points + * @drawable true + */ + meshMeshIntersectionPoints(inputs: Inputs.OCCT.MeshMeshIntersectionTwoShapesDto): Promise{ + return this.occWorkerManager.genericCallToWorkerPromise("booleans.meshMeshIntersectionPoints", inputs); + } + + /** + * Does mesh mesh intersection operation between the shape and multiple other shapes - all shapes can have their own meshing precision. + * This algorithm intersects the meshes and returns the wires of the intersection, which are polylines or polygons. + * @param inputs Two shapes to intersect + * @returns Wires where shapes intersect + * @group mesh based + * @shortname mesh mesh intersection of shapes as wires + * @drawable true + */ + meshMeshIntersectionOfShapesWires(inputs: Inputs.OCCT.MeshMeshesIntersectionOfShapesDto): Promise{ + return this.occWorkerManager.genericCallToWorkerPromise("booleans.meshMeshIntersectionOfShapesWires", inputs); + } + + /** + * Does mesh mesh intersection operation between the shape and multiple other shapes - all shapes can have their own meshing precision. + * This algorithm intersects the meshes and returns the points of the intersection. + * @param inputs Two shapes to intersect + * @returns Wires where shapes intersect + * @group mesh based + * @shortname mesh mesh intersection of shapes as points + * @drawable true + */ + meshMeshIntersectionOfShapesPoints(inputs: Inputs.OCCT.MeshMeshesIntersectionOfShapesDto): Promise{ + return this.occWorkerManager.genericCallToWorkerPromise("booleans.meshMeshIntersectionOfShapesPoints", inputs); + } } diff --git a/packages/dev/occt-worker/lib/api/occt/shapes/face.ts b/packages/dev/occt-worker/lib/api/occt/shapes/face.ts index 3f839f1b..20e1e32e 100644 --- a/packages/dev/occt-worker/lib/api/occt/shapes/face.ts +++ b/packages/dev/occt-worker/lib/api/occt/shapes/face.ts @@ -152,6 +152,18 @@ export class OCCTFace { return this.occWorkerManager.genericCallToWorkerPromise("shapes.face.createCircleFace", inputs); } + /** + * Creates OpenCascade hexagons in grid + * @param inputs Hexagon parameters + * @returns OpenCascade hexagons in grid + * @group primitives + * @shortname hexagons in grid + * @drawable true + */ + hexagonsInGrid(inputs: Inputs.OCCT.HexagonsInGridDto): Promise { + return this.occWorkerManager.genericCallToWorkerPromise("shapes.face.hexagonsInGrid", inputs); + } + /** * Creates OpenCascade ellipse face * @param inputs Ellipse parameters @@ -272,6 +284,30 @@ export class OCCTFace { return this.occWorkerManager.genericCallToWorkerPromise("shapes.face.subdivideToRectangleHoles", inputs); } + /** + * Subdivides a face to hexagon wires + * @param inputs Face and options for subdivision + * @returns wires + * @group patterns + * @shortname hexagon wires on face + * @drawable true + */ + subdivideToHexagonWires(inputs: Inputs.OCCT.FaceSubdivideToHexagonWiresDto): Promise { + return this.occWorkerManager.genericCallToWorkerPromise("shapes.face.subdivideToHexagonWires", inputs); + } + + /** + * Subdivides a face to hexagon holes + * @param inputs Face and options for subdivision + * @returns faces + * @group patterns + * @shortname hexagon holes on face + * @drawable true + */ + subdivideToHexagonHoles(inputs: Inputs.OCCT.FaceSubdivideToHexagonHolesDto): Promise { + return this.occWorkerManager.genericCallToWorkerPromise("shapes.face.subdivideToHexagonHoles", inputs); + } + /** * Subdivides a face to point grid with shifts and removals on nth uv rows or columns * @param inputs Face and params for subdivision diff --git a/packages/dev/occt-worker/lib/api/occt/shapes/wire.ts b/packages/dev/occt-worker/lib/api/occt/shapes/wire.ts index 6d73aab0..45f1e337 100644 --- a/packages/dev/occt-worker/lib/api/occt/shapes/wire.ts +++ b/packages/dev/occt-worker/lib/api/occt/shapes/wire.ts @@ -466,6 +466,18 @@ export class OCCTWire { return this.occWorkerManager.genericCallToWorkerPromise("shapes.wire.createCircleWire", inputs); } + /** + * Creates OpenCascade hexagon wires in grid + * @param inputs grid parameters + * @returns OpenCascade hexagon wires + * @group primitives + * @shortname hegagons in grid + * @drawable true + */ + hexagonsInGrid(inputs: Inputs.OCCT.HexagonsInGridDto): Promise { + return this.occWorkerManager.genericCallToWorkerPromise("shapes.wire.hexagonsInGrid", inputs); + } + /** * Creates OpenCascade square wire * @param inputs Square parameters diff --git a/packages/dev/occt-worker/package-lock.json b/packages/dev/occt-worker/package-lock.json index 82017d87..aa76ac54 100644 --- a/packages/dev/occt-worker/package-lock.json +++ b/packages/dev/occt-worker/package-lock.json @@ -1,15 +1,15 @@ { "name": "@bitbybit-dev/occt-worker", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/occt-worker", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" }, "devDependencies": { @@ -1702,16 +1702,16 @@ "dev": true }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "dependencies": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "node_modules/@cspotcode/source-map-support": { @@ -8169,16 +8169,16 @@ "dev": true }, "@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "requires": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "@cspotcode/source-map-support": { diff --git a/packages/dev/occt-worker/package.json b/packages/dev/occt-worker/package.json index c6dbc305..f399ae48 100644 --- a/packages/dev/occt-worker/package.json +++ b/packages/dev/occt-worker/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/occt-worker", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers CAD algorithms using OpenCascade Technology kernel adapted for WebWorker", "main": "index.js", "repository": { @@ -56,7 +56,7 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" }, "devDependencies": { diff --git a/packages/dev/occt/bitbybit-dev-occt/cdn.js b/packages/dev/occt/bitbybit-dev-occt/cdn.js index bd376b38..bf398614 100644 --- a/packages/dev/occt/bitbybit-dev-occt/cdn.js +++ b/packages/dev/occt/bitbybit-dev-occt/cdn.js @@ -2,7 +2,7 @@ import ocFullJS from "./bitbybit-dev-occt.js"; const initOpenCascade = ({ mainJS = ocFullJS, - mainWasm = "https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.2/wasm/bitbybit-dev-occt.90cf0714.wasm", + mainWasm = "https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@0.20.3/wasm/bitbybit-dev-occt.90cf0714.wasm", worker = undefined, libs = [], module = {}, diff --git a/packages/dev/occt/lib/api/inputs/occ-inputs.ts b/packages/dev/occt/lib/api/inputs/occ-inputs.ts index 97f1ad5d..3d29e122 100644 --- a/packages/dev/occt/lib/api/inputs/occ-inputs.ts +++ b/packages/dev/occt/lib/api/inputs/occ-inputs.ts @@ -1120,6 +1120,215 @@ export namespace OCCT { */ offsetFromBorderV = 0; } + export class FaceSubdivideToHexagonWiresDto { + /** + * Provide options without default values + */ + constructor(shape?: T, nrHexagonsU?: number, nrHexagonsV?: number, flatU?: boolean, scalePatternU?: number[], scalePatternV?: number[], filletPattern?: number[], inclusionPattern?: boolean[], offsetFromBorderU?: number, offsetFromBorderV?: number, extendUUp?: boolean, extendUBottom?: boolean, extendVUp?: boolean, extendVBottom?: boolean) { + if (shape !== undefined) { this.shape = shape; } + if (nrHexagonsU !== undefined) { this.nrHexagonsU = nrHexagonsU; } + if (nrHexagonsV !== undefined) { this.nrHexagonsU = nrHexagonsV; } + if (flatU !== undefined) { this.flatU = flatU; } + if (scalePatternU !== undefined) { this.scalePatternU = scalePatternU; } + if (scalePatternV !== undefined) { this.scalePatternV = scalePatternV; } + if (filletPattern !== undefined) { this.filletPattern = filletPattern; } + if (inclusionPattern !== undefined) { this.inclusionPattern = inclusionPattern; } + if (offsetFromBorderU !== undefined) { this.offsetFromBorderU = offsetFromBorderU; } + if (offsetFromBorderV !== undefined) { this.offsetFromBorderV = offsetFromBorderV; } + if (extendUUp !== undefined) { this.extendUUp = extendUUp; } + if (extendUBottom !== undefined) { this.extendUBottom = extendUBottom; } + if (extendVUp !== undefined) { this.extendVUp = extendVUp; } + if (extendVBottom !== undefined) { this.extendVBottom = extendVBottom; } + } + /** + * Openascade Face + * @default undefined + */ + shape?: T; + /** + * Number of hexagons on U direction + * @default 10 + * @minimum 1 + * @maximum Infinity + * @step 1 + */ + nrHexagonsU? = 10; + /** + * Number of hexagons on V direction + * @default 10 + * @minimum 1 + * @maximum Infinity + * @step 1 + */ + nrHexagonsV? = 10; + // /** + // * If true, we will create hexagons with flat tops on U direction + // * @default false + // */ + flatU = false; + /** + * Hexagon scale pattern on u direction - numbers between 0 and 1, if 1 or undefined is used, no scaling is applied + * @default undefined + * @optional true + */ + scalePatternU?: number[]; + /** + * Hexagon scale pattern on v direction - numbers between 0 and 1, if 1 or undefined is used, no scaling is applied + * @default undefined + * @optional true + */ + scalePatternV?: number[]; + /** + * Hexagon fillet scale pattern - numbers between 0 and 1, if 0 is used, no fillet is applied, + * if 1 is used, the fillet will be exactly half of the length of the shortest segment of the hexagon + * @default undefined + * @optional true + */ + filletPattern?: number[]; + /** + * Hexagon inclusion pattern - true means that the hexagon will be included, + * false means that the hexagon will be removed from the face + * @default undefined + * @optional true + */ + inclusionPattern?: boolean[]; + /** + * If offset on U is bigger then 0 we will use a smaller space for hexagons to be placed. This means that even hexagon of U param 1 will be offset from the face border + * That is often required to create a pattern that is not too close to the face border + * It should not be bigger then half of the total width of the face as that will create problems + * @default 0 + * @minimum 0 + * @maximum 0.5 + * @step 0.01 + */ + offsetFromBorderU? = 0; + /** + * If offset on V is bigger then 0 we will use a smaller space for hexagons to be placed. This means that even hexagon of V param 1 will be offset from the face border + * That is often required to create a pattern that is not too close to the face border + * It should not be bigger then half of the total width of the face as that will create problems + * @default 0 + * @minimum 0 + * @maximum 0.5 + * @step 0.01 + */ + offsetFromBorderV? = 0; + /** + * If true, we will extend the hexagons beyond the face u up border by their pointy tops + * @default false + */ + extendUUp? = false; + /** + * If true, we will extend the hexagons beyond the face u bottom border by their pointy tops + * @default false + */ + extendUBottom? = false; + /** + * If true, we will extend the hexagons beyond the face v upper border by their half width + * @default false + */ + extendVUp? = false; + /** + * If true, we will extend the hexagons beyond the face v bottom border by their half width + * @default false + */ + extendVBottom? = false; + } + + export class FaceSubdivideToHexagonHolesDto { + /** + * Provide options without default values + */ + constructor(shape?: T, nrHexagonsU?: number, nrHexagonsV?: number, flatU?: boolean, holesToFaces?: boolean, scalePatternU?: number[], scalePatternV?: number[], filletPattern?: number[], inclusionPattern?: boolean[], offsetFromBorderU?: number, offsetFromBorderV?: number) { + if (shape !== undefined) { this.shape = shape; } + if (nrHexagonsU !== undefined) { this.nrHexagonsU = nrHexagonsU; } + if (nrHexagonsV !== undefined) { this.nrHexagonsU = nrHexagonsV; } + if (flatU !== undefined) { this.flatU = flatU; } + if (holesToFaces !== undefined) { this.holesToFaces = holesToFaces; } + if (scalePatternU !== undefined) { this.scalePatternU = scalePatternU; } + if (scalePatternV !== undefined) { this.scalePatternV = scalePatternV; } + if (filletPattern !== undefined) { this.filletPattern = filletPattern; } + if (inclusionPattern !== undefined) { this.inclusionPattern = inclusionPattern; } + if (offsetFromBorderU !== undefined) { this.offsetFromBorderU = offsetFromBorderU; } + if (offsetFromBorderV !== undefined) { this.offsetFromBorderV = offsetFromBorderV; } + } + /** + * Openascade Face + * @default undefined + */ + shape?: T; + /** + * Number of hexagons on U direction + * @default 10 + * @minimum 1 + * @maximum Infinity + * @step 1 + */ + nrHexagonsU? = 10; + /** + * Number of hexagons on V direction + * @default 10 + * @minimum 1 + * @maximum Infinity + * @step 1 + */ + nrHexagonsV? = 10; + // /** + // * If true, we will create hexagons with flat tops on U direction + // * @default false + // */ + flatU = false; + /** + * If true, we will also create holes as faces + * @default false + */ + holesToFaces? = false; + /** + * Hexagon scale pattern on u direction - numbers between 0 and 1, if 1 or undefined is used, no scaling is applied + * @default undefined + * @optional true + */ + scalePatternU?: number[]; + /** + * Hexagon scale pattern on v direction - numbers between 0 and 1, if 1 or undefined is used, no scaling is applied + * @default undefined + * @optional true + */ + scalePatternV?: number[]; + /** + * Hexagon fillet scale pattern - numbers between 0 and 1, if 0 is used, no fillet is applied, + * if 1 is used, the fillet will be exactly half of the length of the shortest segment of the hexagon + * @default undefined + * @optional true + */ + filletPattern?: number[]; + /** + * Hexagon inclusion pattern - true means that the hexagon will be included, + * false means that the hexagon will be removed from the face + * @default undefined + * @optional true + */ + inclusionPattern?: boolean[]; + /** + * If offset on U is bigger then 0 we will use a smaller space for hexagons to be placed. This means that even hexagon of U param 1 will be offset from the face border + * That is often required to create a pattern that is not too close to the face border + * It should not be bigger then half of the total width of the face as that will create problems + * @default 0 + * @minimum 0 + * @maximum 0.5 + * @step 0.01 + */ + offsetFromBorderU? = 0; + /** + * If offset on V is bigger then 0 we will use a smaller space for hexagons to be placed. This means that even hexagon of V param 1 will be offset from the face border + * That is often required to create a pattern that is not too close to the face border + * It should not be bigger then half of the total width of the face as that will create problems + * @default 0 + * @minimum 0 + * @maximum 0.5 + * @step 0.01 + */ + offsetFromBorderV? = 0; + } export class FaceSubdivideToRectangleHolesDto { /** @@ -3439,6 +3648,97 @@ export namespace OCCT { */ direction: Base.Vector3 = [0, 1, 0]; } + export class HexagonsInGridDto { + constructor(wdith?: number, height?: number, nrHexagonsInHeight?: number, nrHexagonsInWidth?: number, flatTop?: boolean, extendTop?: boolean, extendBottom?: boolean, extendLeft?: boolean, extendRight?: boolean, scalePatternWidth?: number[], scalePatternHeight?: number[], filletPattern?: number[], inclusionPattern?: boolean[]) { + if (wdith !== undefined) { this.width = wdith; } + if (height !== undefined) { this.height = height; } + if (nrHexagonsInHeight !== undefined) { this.nrHexagonsInHeight = nrHexagonsInHeight; } + if (nrHexagonsInWidth !== undefined) { this.nrHexagonsInWidth = nrHexagonsInWidth; } + if (flatTop !== undefined) { this.flatTop = flatTop; } + if (extendTop !== undefined) { this.extendTop = extendTop; } + if (extendBottom !== undefined) { this.extendBottom = extendBottom; } + if (extendLeft !== undefined) { this.extendLeft = extendLeft; } + if (extendRight !== undefined) { this.extendRight = extendRight; } + if (scalePatternWidth !== undefined) { this.scalePatternWidth = scalePatternWidth; } + if (scalePatternHeight !== undefined) { this.scalePatternHeight = scalePatternHeight; } + if (filletPattern !== undefined) { this.filletPattern = filletPattern; } + if (inclusionPattern !== undefined) { this.inclusionPattern = inclusionPattern; } + } + /** Total desired width for the grid area. The hexagon size will be derived from this and nrHexagonsU. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 0.1 + */ + width? = 10; + /** Total desired height for the grid area. Note: due to hexagon geometry, the actual grid height might differ slightly if maintaining regular hexagons based on width. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 0.1 + */ + height? = 10; + /** Number of hexagons desired in width. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + nrHexagonsInWidth? = 10; + /** Number of hexagons desired in height. + * @default 10 + * @minimum 0 + * @maximum Infinity + * @step 1 + */ + nrHexagonsInHeight? = 10; + /** If true, the hexagons will be oriented with their flat sides facing up and down. + * @default false + */ + flatTop? = false; + /** If true, shift the entire grid up by half hex height. + * @default false + */ + extendTop? = false; + /** If true, shift the entire grid down by half hex height. + * @default false + */ + extendBottom? = false; + /** If true, shift the entire grid left by half hex width. + * @default false + */ + extendLeft? = false; + /** If true, shift the entire grid right by half hex width. + * @default false + */ + extendRight? = false; + /** + * Hex scale pattern on width direction - numbers between 0 and 1, if 1 or undefined is used, no scaling is applied + * @default undefined + * @optional true + */ + scalePatternWidth?: number[]; + /** + * Hex scale pattern on height direction - numbers between 0 and 1, if 1 or undefined is used, no scaling is applied + * @default undefined + * @optional true + */ + scalePatternHeight?: number[]; + /** + * Hex fillet scale pattern - numbers between 0 and 1, if 0 is used, no fillet is applied, + * if 1 is used, the fillet will be exactly half of the length of the shorter side of the hex + * @default undefined + * @optional true + */ + filletPattern?: number[]; + /** + * Inclusion pattern - true means that the hex will be included, + * false means that the hex will be removed + * @default undefined + * @optional true + */ + inclusionPattern?: boolean[]; + } export class LoftDto { constructor(shapes?: T[], makeSolid?: boolean) { if (shapes !== undefined) { this.shapes = shapes; } @@ -3933,6 +4233,75 @@ export namespace OCCT { */ shape: T; } + export class MeshMeshIntersectionTwoShapesDto { + constructor(shape1?: T, shape2?: T, precision1?: number, precision2?: number) { + if (shape1 !== undefined) { this.shape1 = shape1; } + if (shape2 !== undefined) { this.shape2 = shape2; } + if (precision1 !== undefined) { this.precision1 = precision1; } + if (precision2 !== undefined) { this.precision2 = precision2; } + } + /** + * First shape to be used for intersection + * @default undefined + */ + shape1: T; + /** + * Precision of first shape to be used for meshing and computing intersection. + * Keep in mind that the lower this value is, the more triangles will be produced and thus the slower the computation. + * @default 0.01 + * @minimum 0 + * @maximum Infinity + * @step 0.01 + */ + precision1? = 0.01; + /** + * Second shape to be used for intersection + * @default undefined + */ + shape2: T; + /** + * Precision of second shape to be used for meshing and computing intersection. + * Keep in mind that the lower this value is, the more triangles will be produced and thus the slower the computation. + * @default 0.01 + * @minimum 0 + * @maximum Infinity + * @step 0.01 + */ + precision2? = 0.01; + } + export class MeshMeshesIntersectionOfShapesDto { + constructor(shape?: T, shapes?: T[], precision?: number, precisionShapes?: number[]) { + if (shape !== undefined) { this.shape = shape; } + if (shapes !== undefined) { this.shapes = shapes; } + if (precision !== undefined) { this.precision = precision; } + if (precisionShapes !== undefined) { this.precisionShapes = precisionShapes; } + } + /** + * Shape to use for the base of computations + * @default undefined + */ + shape?: T; + /** + * Precision of first shape to be used for meshing and computing intersection. + * Keep in mind that the lower this value is, the more triangles will be produced and thus the slower the computation. + * @default 0.01 + * @minimum 0 + * @maximum Infinity + * @step 0.01 + */ + precision? = 0.01; + /** + * Second shape to be used for intersection + * @default undefined + */ + shapes?: T[]; + /** + * Precision of shapes to be used, if undefined, a universal precision will be used of the first shape + * @default undefined + * @optional true + */ + precisionShapes?: number[]; + } export class CompareShapesDto { constructor(shape?: T, otherShape?: T) { if (shape !== undefined) { this.shape = shape; } diff --git a/packages/dev/occt/lib/occ-helper.ts b/packages/dev/occt/lib/occ-helper.ts index fd017d6f..08a023bc 100644 --- a/packages/dev/occt/lib/occ-helper.ts +++ b/packages/dev/occt/lib/occ-helper.ts @@ -19,8 +19,9 @@ import { VerticesService } from "./services/base/vertices.service"; import { ShellsService } from "./services/base/shells.service"; import { FilletsService } from "./services/base/fillets.service"; import { SolidsService } from "./services/base/solids.service"; -import { TextBitByBit, Point, GeometryHelper, Transforms, Vector, MathBitByBit } from "@bitbybit-dev/base"; +import { TextBitByBit, Point, GeometryHelper, Transforms, Vector, MathBitByBit, MeshBitByBit, Polyline, Line, Lists } from "@bitbybit-dev/base"; import { DimensionsService } from "./services/base/dimensions.service"; +import { MeshingService } from "./services/base/meshing.service"; export class OccHelper { @@ -30,7 +31,11 @@ export class OccHelper { private readonly vector: Vector; private readonly math: MathBitByBit; private readonly transforms: Transforms; + private readonly lists: Lists; private readonly point: Point; + private readonly line: Line; + private readonly polyline: Polyline; + private readonly mesh: MeshBitByBit; private readonly textService: TextBitByBit; public readonly iteratorService: IteratorService; @@ -50,6 +55,7 @@ export class OccHelper { public readonly solidsService: SolidsService; public readonly operationsService: OperationsService; public readonly filletsService: FilletsService; + public readonly meshingService: MeshingService; public readonly dimensionsService: DimensionsService; @@ -63,8 +69,12 @@ export class OccHelper { this.math = new MathBitByBit(); this.vector = new Vector(this.math, this.geometryHelper); this.transforms = new Transforms(this.vector, this.math); - this.point = new Point(this.geometryHelper, this.transforms, this.vector); + this.lists = new Lists(); + this.point = new Point(this.geometryHelper, this.transforms, this.vector, this.lists); + this.line = new Line(this.vector, this.point, this.geometryHelper); + this.polyline = new Polyline(this.vector, this.point, this.line, this.geometryHelper); this.textService = new TextBitByBit(this.point); + this.mesh = new MeshBitByBit(this.vector, this.polyline); this.occRefReturns = new OCCReferencedReturns(occ); this.iteratorService = new IteratorService(occ); @@ -73,7 +83,6 @@ export class OccHelper { this.entitiesService = new EntitiesService(occ); this.shapeGettersService = new ShapeGettersService(occ, this.enumService, this.iteratorService); this.geomService = new GeomService(occ, this.vecHelper, this.entitiesService); - this.booleansService = new BooleansService(occ, this.shapeGettersService); this.transformsService = new TransformsService(occ, this.converterService, this.entitiesService, this.vecHelper); this.verticesService = new VerticesService(occ, this.entitiesService, this.converterService, this.shapeGettersService, this.wiresService, this.booleansService); @@ -81,17 +90,21 @@ export class OccHelper { this.edgesService = new EdgesService(occ, this.occRefReturns, this.shapeGettersService, this.entitiesService, this.iteratorService, this.converterService, this.enumService, this.geomService, this.transformsService, this.vecHelper); - this.wiresService = new WiresService(occ, this.occRefReturns, this.vector, this.shapesHelperService, this.shapeGettersService, this.transformsService, - this.enumService, this.entitiesService, this.converterService, this.geomService, this.edgesService, this.textService, this.operationsService); + this.wiresService = new WiresService(occ, this.occRefReturns, this.vector, this.point, this.shapesHelperService, this.shapeGettersService, this.transformsService, + this.enumService, this.entitiesService, this.converterService, this.geomService, this.edgesService, this.textService, this.filletsService, this.operationsService); this.dimensionsService = new DimensionsService(this.math, this.vector, this.point, this.transformsService, this.converterService, this.entitiesService, this.edgesService, this.wiresService); + this.meshingService = new MeshingService(occ, this.shapeGettersService, this.transformsService, this.edgesService, this.facesService, this.wiresService, this.mesh); + + this.booleansService = new BooleansService(occ, this.shapeGettersService); this.verticesService.wiresService = this.wiresService; + this.verticesService.booleansService = this.booleansService; this.facesService = new FacesService(occ, this.occRefReturns, this.entitiesService, this.enumService, - this.shapeGettersService, this.converterService, this.booleansService, this.wiresService, this.transformsService, this.vecHelper, this.filletsService); - + this.shapeGettersService, this.converterService, this.booleansService, this.wiresService, this.transformsService, this.vecHelper, this.point, this.filletsService); + this.meshingService.facesService = this.facesService; this.shellsService = new ShellsService(occ, this.shapeGettersService, this.converterService, this.facesService); this.solidsService = new SolidsService(occ, this.shapeGettersService, this.facesService, this.enumService, @@ -105,7 +118,7 @@ export class OccHelper { this.filletsService = new FilletsService(occ, this.vecHelper, this.iteratorService, this.converterService, this.entitiesService, this.transformsService, this.shapeGettersService, this.edgesService, this.operationsService, this.facesService); - + this.wiresService.filletsService = this.filletsService; // cross reference this.facesService.filletsService = this.filletsService; } diff --git a/packages/dev/occt/lib/occ-service.ts b/packages/dev/occt/lib/occ-service.ts index b5ed2483..426d8cc8 100644 --- a/packages/dev/occt/lib/occ-service.ts +++ b/packages/dev/occt/lib/occ-service.ts @@ -1,4 +1,4 @@ -import { OpenCascadeInstance, Handle_Poly_Triangulation, TopoDS_Shape } from "../bitbybit-dev-occt/bitbybit-dev-occt"; +import { OpenCascadeInstance, TopoDS_Shape } from "../bitbybit-dev-occt/bitbybit-dev-occt"; import * as Inputs from "./api/inputs/inputs"; import { OCCTBooleans } from "./services/booleans"; import { OCCTGeom } from "./services/geom/geom"; @@ -43,28 +43,7 @@ export class OCCTService { } shapeFacesToPolygonPoints(inputs: Inputs.OCCT.ShapeFacesToPolygonPointsDto): Inputs.Base.Point3[][] { - const def = this.shapeToMesh(inputs); - const res = []; - def.faceList.forEach(face => { - const vertices = face.vertex_coord; - const indices = face.tri_indexes; - for (let i = 0; i < indices.length; i += 3) { - const p1 = indices[i]; - const p2 = indices[i + 1]; - const p3 = indices[i + 2]; - let pts =[ - [vertices[p1 * 3], vertices[p1 * 3 + 1], vertices[p1 * 3 + 2]], - [vertices[p2 * 3], vertices[p2 * 3 + 1], vertices[p2 * 3 + 2]], - [vertices[p3 * 3], vertices[p3 * 3 + 1], vertices[p3 * 3 + 2]], - ]; - if(inputs.reversedPoints){ - pts = pts.reverse(); - } - res.push(pts); - } - }); - - return res; + return this.och.meshingService.shapeFacesToPolygonPoints(inputs); } shapesToMeshes(inputs: Inputs.OCCT.ShapesToMeshesDto): Inputs.OCCT.DecomposedMeshDto[] { @@ -72,174 +51,7 @@ export class OCCTService { } shapeToMesh(inputs: Inputs.OCCT.ShapeToMeshDto): Inputs.OCCT.DecomposedMeshDto { - - const faceList: Inputs.OCCT.DecomposedFaceDto[] = []; - const edgeList: Inputs.OCCT.DecomposedEdgeDto[] = []; - const pointsList: Inputs.Base.Point3[] = []; - let shapeToUse = inputs.shape; - if (shapeToUse.IsNull()) return { faceList, edgeList, pointsList: [] }; - - // This could be made optional... - // Clean cached triangulation data for the shape. - // This allows to get lower res models out of higher res that was once computed and cached. - this.occ.BRepTools.Clean(shapeToUse, true); - - if (inputs.adjustYtoZ) { - const shapeToUseRotated = this.och.transformsService.rotate({ shape: inputs.shape, axis: [1, 0, 0], angle: -90 }); - const shapeMirrored = this.och.transformsService.mirrorAlongNormal( - { shape: shapeToUseRotated, origin: [0, 0, 0], normal: [0, 0, 1] } - ); - shapeToUseRotated.delete(); - shapeToUse.delete(); - shapeToUse = shapeMirrored; - } - - // Iterate through the faces and triangulate each one - const triangulations: Handle_Poly_Triangulation[] = []; - const faces = this.och.shapeGettersService.getFaces({ shape: shapeToUse }); - - let incrementalMeshBuilder; - if (faces && faces.length) { - incrementalMeshBuilder = new this.occ.BRepMesh_IncrementalMesh_2(shapeToUse, inputs.precision, false, 0.5, false); - faces.forEach((myFace, faceIndex) => { - - const aLocation = new this.occ.TopLoc_Location_1(); - const myT = this.occ.BRep_Tool.Triangulation(myFace, aLocation, 0); - if (myT.IsNull()) { console.error("Encountered Null Face!"); return; } - - const thisFace: Inputs.OCCT.DecomposedFaceDto = { - vertex_coord: [], - normal_coord: [], - uvs: [], - tri_indexes: [], - vertex_coord_vec: [], - number_of_triangles: 0, - center_point: undefined, - center_normal: undefined, - face_index: faceIndex - }; - - thisFace.center_point = this.och.facesService.pointOnUV({ shape: myFace, paramU: 0.5, paramV: 0.5 }); - thisFace.center_normal = this.och.facesService.normalOnUV({ shape: myFace, paramU: 0.5, paramV: 0.5 }); - - const pc = new this.occ.Poly_Connect_2(myT); - const triangulation = myT.get(); - - // write vertex buffer - thisFace.vertex_coord = new Array(triangulation.NbNodes() * 3); - thisFace.vertex_coord_vec = []; - for (let i = 0; i < triangulation.NbNodes(); i++) { - const p = triangulation.Node(i + 1).Transformed(aLocation.Transformation()); - const uv = triangulation.UVNode(i + 1); - thisFace.uvs[(i * 2) + 0] = uv.X(); - thisFace.uvs[(i * 2) + 1] = uv.Y(); - thisFace.vertex_coord[(i * 3) + 0] = p.X(); - thisFace.vertex_coord[(i * 3) + 1] = p.Y(); - thisFace.vertex_coord[(i * 3) + 2] = p.Z(); - thisFace.vertex_coord_vec.push([p.X(), p.Y(), p.Z()]); - } - - // write normal buffer - const myNormal = new this.occ.TColgp_Array1OfDir_2(1, triangulation.NbNodes()); - this.occ.StdPrs_ToolTriangulatedShape.Normal(myFace, pc, myNormal); - thisFace.normal_coord = new Array(myNormal.Length() * 3); - for (let i = 0; i < myNormal.Length(); i++) { - const d = myNormal.Value(i + 1); - thisFace.normal_coord[(i * 3) + 0] = d.X(); - thisFace.normal_coord[(i * 3) + 1] = d.Y(); - thisFace.normal_coord[(i * 3) + 2] = d.Z(); - } - - // write triangle buffer - const orient = myFace.Orientation_1(); - const triangles = myT.get().Triangles(); - thisFace.tri_indexes = new Array(triangles.Length() * 3); - let validFaceTriCount = 0; - for (let nt = 1; nt <= myT.get().NbTriangles(); nt++) { - const t = triangles.Value(nt); - let n1 = t.Value(1); - let n2 = t.Value(2); - const n3 = t.Value(3); - if (orient !== this.occ.TopAbs_Orientation.TopAbs_FORWARD) { - const tmp = n1; - n1 = n2; - n2 = tmp; - } - thisFace.tri_indexes[(validFaceTriCount * 3) + 0] = n1 - 1; - thisFace.tri_indexes[(validFaceTriCount * 3) + 1] = n2 - 1; - thisFace.tri_indexes[(validFaceTriCount * 3) + 2] = n3 - 1; - validFaceTriCount++; - } - thisFace.number_of_triangles = validFaceTriCount; - faceList.push(thisFace); - - triangulations.push(myT); - - aLocation.delete(); - myNormal.delete(); - triangles.delete(); - pc.delete(); - - }); - } - - // Nullify Triangulations between runs so they're not stored in the cache - for (let i = 0; i < triangulations.length; i++) { - triangulations[i].Nullify(); - triangulations[i].delete(); - } - - // Get the free edges that aren't on any triangulated face/surface - const edges = this.och.shapeGettersService.getEdges({ shape: shapeToUse }); - edges.forEach((myEdge, index) => { - const thisEdge: Inputs.OCCT.DecomposedEdgeDto = { - vertex_coord: [], - middle_point: undefined, - edge_index: -1 - }; - - thisEdge.middle_point = this.och.edgesService.pointOnEdgeAtParam({ shape: myEdge, param: 0.5 }); - const aLocation = new this.occ.TopLoc_Location_1(); - const adaptorCurve = new this.occ.BRepAdaptor_Curve_2(myEdge); - const tangDef = new this.occ.GCPnts_TangentialDeflection_2(adaptorCurve, inputs.precision, 0.1, 2, 1.0e-9, 1.0e-7); - - // write vertex buffer - thisEdge.vertex_coord = []; - const nrPoints = tangDef.NbPoints(); - const tangDefValues = []; - for (let j = 0; j < nrPoints; j++) { - const tangDefVal = tangDef.Value(j + 1); - thisEdge.vertex_coord.push([ - tangDefVal.X(), - tangDefVal.Y(), - tangDefVal.Z() - ]); - tangDefValues.push(tangDefVal); - } - thisEdge.edge_index = index; - - edgeList.push(thisEdge); - tangDefValues.forEach(v => v.delete()); - aLocation.delete(); - adaptorCurve.delete(); - tangDef.delete(); - this.occ.BRepTools.Clean(myEdge, true); - }); - - const vertices = this.och.shapeGettersService.getVertices({ shape: shapeToUse }); - if (vertices.length > 0) { - vertices.forEach(v => { - const pt = this.occ.BRep_Tool.Pnt(v); - pointsList.push([pt.X(), pt.Y(), pt.Z()]); - pt.delete(); - }); - } - - if (incrementalMeshBuilder) { - incrementalMeshBuilder.Delete(); - } - this.occ.BRepTools.Clean(shapeToUse, true); - return { faceList, edgeList, pointsList }; + return this.och.meshingService.shapeToMesh(inputs); } diff --git a/packages/dev/occt/lib/services/base/entities.service.ts b/packages/dev/occt/lib/services/base/entities.service.ts index d85118bd..2b29c71e 100644 --- a/packages/dev/occt/lib/services/base/entities.service.ts +++ b/packages/dev/occt/lib/services/base/entities.service.ts @@ -332,4 +332,6 @@ export class EntitiesService { return new this.occ.Handle_Geom_Curve_2(curve); } + + } diff --git a/packages/dev/occt/lib/services/base/faces.service.ts b/packages/dev/occt/lib/services/base/faces.service.ts index 14fde819..f0bb7c39 100644 --- a/packages/dev/occt/lib/services/base/faces.service.ts +++ b/packages/dev/occt/lib/services/base/faces.service.ts @@ -11,6 +11,7 @@ import { ConverterService } from "./converter.service"; import { FilletsService } from "./fillets.service"; import { TransformsService } from "./transforms.service"; import { VectorHelperService } from "../../api"; +import { Point } from "@bitbybit-dev/base"; export class FacesService { @@ -21,10 +22,11 @@ export class FacesService { private readonly enumService: EnumService, private readonly shapeGettersService: ShapeGettersService, private readonly converterService: ConverterService, - private readonly booleansService: BooleansService, + public booleansService: BooleansService, private readonly wiresService: WiresService, private readonly transformsService: TransformsService, private readonly vectorService: VectorHelperService, + private readonly point: Point, public filletsService: FilletsService, ) { } @@ -735,6 +737,211 @@ export class FacesService { return [newFace, ...faces]; } + + subdivideToHexagonWires(inputs: Inputs.OCCT.FaceSubdivideToHexagonWiresDto): TopoDS_Wire[] { + if (inputs.shape === undefined) { + throw new Error("Face not defined"); + } + const shapesToDelete: TopoDS_Shape[] = []; + const face = inputs.shape; + const handle = this.occ.BRep_Tool.Surface_2(face); + const surface = handle.get(); + const { uMin, uMax, vMin, vMax } = this.getUVBounds(face); + + // Calculate parametric range + const scaleU = uMax - uMin; + const scaleV = vMax - vMin; + + if (scaleU <= 0 || scaleV <= 0) { + console.warn("Face has zero or negative parametric range. Skipping."); + return []; + } + + // Calculate target parametric dimensions and origin for the grid + const gridHeightU = scaleU * (1 - inputs.offsetFromBorderU * 2); + const gridWidthV = scaleV * (1 - inputs.offsetFromBorderV * 2); + + const gridOriginU = uMin + scaleU * inputs.offsetFromBorderU; + const gridOriginV = vMin + scaleV * inputs.offsetFromBorderV; + + if (gridHeightU <= 0 || gridWidthV <= 0) { + console.warn("Grid dimensions are zero or negative after applying offset. Skipping."); + return []; + } + + // Generate hexagon grid in local 2D space (assuming X maps to V, Z maps to U) + const hex = this.point.hexGridScaledToFit({ + width: gridWidthV, + height: gridHeightU, + nrHexagonsInHeight: inputs.nrHexagonsU, + nrHexagonsInWidth: inputs.nrHexagonsV, + centerGrid: false, + pointsOnGround: true, + flatTop: inputs.flatU, + extendTop: inputs.extendUUp, + extendBottom: inputs.extendUBottom, + extendLeft: inputs.extendVBottom, + extendRight: inputs.extendVUp, + }); + + // Create wires in local 2D space + const localHexWires = hex.hexagons.map(hexPoints => { + return this.wiresService.createPolygonWire({ + points: hexPoints + }); + }); + shapesToDelete.push(...localHexWires); + + // Define the translation vector to map local grid origin to parametric grid origin + const uvTranslation = [gridOriginV, 0, gridOriginU] as Base.Vector3; + + // Translate wires to parametric UV space + const uvHexWires = localHexWires.map(h => { + return this.transformsService.translate({ + shape: h, + translation: uvTranslation + }); + }); + shapesToDelete.push(...uvHexWires); + + // Translate centers to parametric UV space + const uvHexCenters = this.point.translatePoints({ + points: hex.centers, + translation: uvTranslation + }); + + const finalPlacedWires = []; + + let currentScalePatternUIndex = 0; + let currentScalePatternVIndex = 0; + let currentInclusionPatternIndex = 0; + let currentFilletPatternIndex = 0; + + // Ensure we have enough hexagons generated for the loop counts + const totalHexagons = inputs.nrHexagonsU * inputs.nrHexagonsV; + if (uvHexWires.length !== totalHexagons || uvHexCenters.length !== totalHexagons) { + console.error(`Generated ${uvHexWires.length} hexagons, but expected ${totalHexagons}. Check hexGridScaledToFit logic.`); + return []; + } + + // Process each hexagon (scale, fillet, place) + for (let i = 0; i < inputs.nrHexagonsU; i++) { + for (let j = 0; j < inputs.nrHexagonsV; j++) { + const hexIndex = i * inputs.nrHexagonsV + j; + + // Get scale/inclusion/fillet values from patterns + let scaleFromPatternU = 1; + if (inputs.scalePatternU?.length > 0) { + scaleFromPatternU = inputs.scalePatternU[currentScalePatternUIndex % inputs.scalePatternU.length]; + currentScalePatternUIndex++; + } + + let scaleFromPatternV = 1; + if (inputs.scalePatternV?.length > 0) { + scaleFromPatternV = inputs.scalePatternV[currentScalePatternVIndex % inputs.scalePatternV.length]; + currentScalePatternVIndex++; + } + + let include = true; + if (inputs.inclusionPattern?.length > 0) { + include = inputs.inclusionPattern[currentInclusionPatternIndex % inputs.inclusionPattern.length]; + currentInclusionPatternIndex++; + } + + let filletFactor = 0; + if (inputs.filletPattern?.length > 0) { + filletFactor = inputs.filletPattern[currentFilletPatternIndex % inputs.filletPattern.length]; + currentFilletPatternIndex++; + } + + if (include) { + const uvHexagon = uvHexWires[hexIndex]; + const uvCenter = uvHexCenters[hexIndex]; + + let shapeToScale = uvHexagon; + // Apply Fillet (using the factor) + const filletRadius = hex.maxFilletRadius * filletFactor; + if (filletRadius > 1e-6) { + const filletedHex = this.filletsService.fillet2d({ + shape: uvHexagon, + radius: filletRadius, + }); + shapesToDelete.push(filletedHex); + shapeToScale = filletedHex; + } + + // Apply Scaling (around the correct UV center) + let shapeToPlace = shapeToScale; + // Scaling vector maps to V + const scaleVec = [scaleFromPatternV, 1, scaleFromPatternU] as Base.Vector3; + if (Math.abs(scaleFromPatternU - 1.0) > 1e-6 || Math.abs(scaleFromPatternV - 1.0) > 1e-6) { + const scaledHex = this.transformsService.scale3d({ + shape: shapeToScale, + center: uvCenter, + scale: scaleVec, + }); + shapesToDelete.push(scaledHex); + shapeToPlace = scaledHex; + } + + // Place the final processed wire onto the surface + const placedWire = this.wiresService.placeWire(shapeToPlace, surface); + finalPlacedWires.push(placedWire); + + } + } + } + + shapesToDelete.forEach(s => { + s.delete(); + }); + + return finalPlacedWires; + } + + subdivideToHexagonHoles(inputs: Inputs.OCCT.FaceSubdivideToHexagonHolesDto): TopoDS_Wire[] { + if (inputs.scalePatternU === undefined) { + inputs.scalePatternU = [0.5]; + } + if (inputs.scalePatternV === undefined) { + inputs.scalePatternV = [0.5]; + } + const wires = this.subdivideToHexagonWires(inputs); + const faceWires = this.shapeGettersService.getWires({ shape: inputs.shape }); + const wireLengths = this.wiresService.getWiresLengths({ shapes: faceWires }); + const longestFaceWire = faceWires[wireLengths.indexOf(Math.max(...wireLengths))]; + + const revWires = wires.map(wire => { return this.wiresService.reversedWire({ shape: wire }); }); + const listOfWires = [longestFaceWire, ...revWires]; + const newFace = this.createFaceFromWiresOnFace({ wires: listOfWires, face: inputs.shape, inside: true }); + + // check if the normals are the same, if not reverse the face + const normalOriginal = this.faceNormalOnUV({ shape: inputs.shape, paramU: 0, paramV: 0 }); + const normalNew = this.faceNormalOnUV({ shape: newFace, paramU: 0, paramV: 0 }); + + let shouldReverse = false; + if (this.vectorService.angleBetweenVectors(normalOriginal, normalNew) > 1e-7) { + shouldReverse = true; + newFace.Reverse(); + } + + let faces = []; + if (inputs.holesToFaces) { + faces = wires.map(wire => { + return this.createFaceFromWireOnFace({ wire, face: inputs.shape, inside: true }); + }); + if (shouldReverse) { + faces.forEach(f => f.Reverse()); + } + } + + wires.forEach(w => w.delete()); + longestFaceWire.delete(); + revWires.forEach(w => w.delete()); + + return [newFace, ...faces]; + } + subdivideToNormals(inputs: Inputs.OCCT.FaceSubdivisionDto): Base.Point3[] { if (inputs.shape === undefined) { throw (Error(("Face not defined"))); diff --git a/packages/dev/occt/lib/services/base/meshing.service.ts b/packages/dev/occt/lib/services/base/meshing.service.ts new file mode 100644 index 00000000..987c2a0a --- /dev/null +++ b/packages/dev/occt/lib/services/base/meshing.service.ts @@ -0,0 +1,306 @@ +import { MeshBitByBit } from "@bitbybit-dev/base"; +import { + Handle_Poly_Triangulation, + OpenCascadeInstance, + TopoDS_Shape, TopoDS_Wire +} from "../../../bitbybit-dev-occt/bitbybit-dev-occt"; +import * as Inputs from "../../api/inputs/inputs"; +import { EdgesService } from "./edges.service"; +import { FacesService } from "./faces.service"; +import { ShapeGettersService } from "./shape-getters"; +import { TransformsService } from "./transforms.service"; +import { WiresService } from "./wires.service"; + +export class MeshingService { + + constructor( + public readonly occ: OpenCascadeInstance, + public readonly shapeGettersService: ShapeGettersService, + public readonly transformsService: TransformsService, + public readonly edgesService: EdgesService, + public facesService: FacesService, + public readonly wiresService: WiresService, + public readonly mesh: MeshBitByBit + ) { } + + shapeFacesToPolygonPoints(inputs: Inputs.OCCT.ShapeFacesToPolygonPointsDto): Inputs.Base.Point3[][] { + const def = this.shapeToMesh(inputs); + const res = []; + def.faceList.forEach(face => { + const vertices = face.vertex_coord; + const indices = face.tri_indexes; + for (let i = 0; i < indices.length; i += 3) { + const p1 = indices[i]; + const p2 = indices[i + 1]; + const p3 = indices[i + 2]; + let pts = [ + [vertices[p1 * 3], vertices[p1 * 3 + 1], vertices[p1 * 3 + 2]], + [vertices[p2 * 3], vertices[p2 * 3 + 1], vertices[p2 * 3 + 2]], + [vertices[p3 * 3], vertices[p3 * 3 + 1], vertices[p3 * 3 + 2]], + ]; + if (inputs.reversedPoints) { + pts = pts.reverse(); + } + res.push(pts); + } + }); + + return res; + } + + shapesToMeshes(inputs: Inputs.OCCT.ShapesToMeshesDto): Inputs.OCCT.DecomposedMeshDto[] { + return inputs.shapes.map(shape => this.shapeToMesh({ shape, precision: inputs.precision, adjustYtoZ: inputs.adjustYtoZ })); + } + + shapeToMesh(inputs: Inputs.OCCT.ShapeToMeshDto): Inputs.OCCT.DecomposedMeshDto { + + const faceList: Inputs.OCCT.DecomposedFaceDto[] = []; + const edgeList: Inputs.OCCT.DecomposedEdgeDto[] = []; + const pointsList: Inputs.Base.Point3[] = []; + let shapeToUse = inputs.shape; + if (shapeToUse.IsNull()) return { faceList, edgeList, pointsList: [] }; + + // This could be made optional... + // Clean cached triangulation data for the shape. + // This allows to get lower res models out of higher res that was once computed and cached. + this.occ.BRepTools.Clean(shapeToUse, true); + + if (inputs.adjustYtoZ) { + const shapeToUseRotated = this.transformsService.rotate({ shape: inputs.shape, axis: [1, 0, 0], angle: -90 }); + const shapeMirrored = this.transformsService.mirrorAlongNormal( + { shape: shapeToUseRotated, origin: [0, 0, 0], normal: [0, 0, 1] } + ); + shapeToUseRotated.delete(); + shapeToUse.delete(); + shapeToUse = shapeMirrored; + } + + // Iterate through the faces and triangulate each one + const triangulations: Handle_Poly_Triangulation[] = []; + const faces = this.shapeGettersService.getFaces({ shape: shapeToUse }); + + let incrementalMeshBuilder; + if (faces && faces.length) { + incrementalMeshBuilder = new this.occ.BRepMesh_IncrementalMesh_2(shapeToUse, inputs.precision, false, 0.5, false); + faces.forEach((myFace, faceIndex) => { + + const aLocation = new this.occ.TopLoc_Location_1(); + const myT = this.occ.BRep_Tool.Triangulation(myFace, aLocation, 0); + if (myT.IsNull()) { console.error("Encountered Null Face!"); return; } + + const thisFace: Inputs.OCCT.DecomposedFaceDto = { + vertex_coord: [], + normal_coord: [], + uvs: [], + tri_indexes: [], + vertex_coord_vec: [], + number_of_triangles: 0, + center_point: undefined, + center_normal: undefined, + face_index: faceIndex + }; + + thisFace.center_point = this.facesService.pointOnUV({ shape: myFace, paramU: 0.5, paramV: 0.5 }); + thisFace.center_normal = this.facesService.normalOnUV({ shape: myFace, paramU: 0.5, paramV: 0.5 }); + + const pc = new this.occ.Poly_Connect_2(myT); + const triangulation = myT.get(); + + // write vertex buffer + thisFace.vertex_coord = new Array(triangulation.NbNodes() * 3); + thisFace.vertex_coord_vec = []; + for (let i = 0; i < triangulation.NbNodes(); i++) { + const p = triangulation.Node(i + 1).Transformed(aLocation.Transformation()); + const uv = triangulation.UVNode(i + 1); + thisFace.uvs[(i * 2) + 0] = uv.X(); + thisFace.uvs[(i * 2) + 1] = uv.Y(); + thisFace.vertex_coord[(i * 3) + 0] = p.X(); + thisFace.vertex_coord[(i * 3) + 1] = p.Y(); + thisFace.vertex_coord[(i * 3) + 2] = p.Z(); + thisFace.vertex_coord_vec.push([p.X(), p.Y(), p.Z()]); + } + + // write normal buffer + const myNormal = new this.occ.TColgp_Array1OfDir_2(1, triangulation.NbNodes()); + this.occ.StdPrs_ToolTriangulatedShape.Normal(myFace, pc, myNormal); + thisFace.normal_coord = new Array(myNormal.Length() * 3); + for (let i = 0; i < myNormal.Length(); i++) { + const d = myNormal.Value(i + 1); + thisFace.normal_coord[(i * 3) + 0] = d.X(); + thisFace.normal_coord[(i * 3) + 1] = d.Y(); + thisFace.normal_coord[(i * 3) + 2] = d.Z(); + } + + // write triangle buffer + const orient = myFace.Orientation_1(); + const triangles = myT.get().Triangles(); + thisFace.tri_indexes = new Array(triangles.Length() * 3); + let validFaceTriCount = 0; + for (let nt = 1; nt <= myT.get().NbTriangles(); nt++) { + const t = triangles.Value(nt); + let n1 = t.Value(1); + let n2 = t.Value(2); + const n3 = t.Value(3); + if (orient !== this.occ.TopAbs_Orientation.TopAbs_FORWARD) { + const tmp = n1; + n1 = n2; + n2 = tmp; + } + thisFace.tri_indexes[(validFaceTriCount * 3) + 0] = n1 - 1; + thisFace.tri_indexes[(validFaceTriCount * 3) + 1] = n2 - 1; + thisFace.tri_indexes[(validFaceTriCount * 3) + 2] = n3 - 1; + validFaceTriCount++; + } + thisFace.number_of_triangles = validFaceTriCount; + faceList.push(thisFace); + + triangulations.push(myT); + + aLocation.delete(); + myNormal.delete(); + triangles.delete(); + pc.delete(); + + }); + } + + // Nullify Triangulations between runs so they're not stored in the cache + for (let i = 0; i < triangulations.length; i++) { + triangulations[i].Nullify(); + triangulations[i].delete(); + } + + // Get the free edges that aren't on any triangulated face/surface + const edges = this.shapeGettersService.getEdges({ shape: shapeToUse }); + edges.forEach((myEdge, index) => { + const thisEdge: Inputs.OCCT.DecomposedEdgeDto = { + vertex_coord: [], + middle_point: undefined, + edge_index: -1 + }; + + thisEdge.middle_point = this.edgesService.pointOnEdgeAtParam({ shape: myEdge, param: 0.5 }); + const aLocation = new this.occ.TopLoc_Location_1(); + const adaptorCurve = new this.occ.BRepAdaptor_Curve_2(myEdge); + const tangDef = new this.occ.GCPnts_TangentialDeflection_2(adaptorCurve, inputs.precision, 0.1, 2, 1.0e-9, 1.0e-7); + + // write vertex buffer + thisEdge.vertex_coord = []; + const nrPoints = tangDef.NbPoints(); + const tangDefValues = []; + for (let j = 0; j < nrPoints; j++) { + const tangDefVal = tangDef.Value(j + 1); + thisEdge.vertex_coord.push([ + tangDefVal.X(), + tangDefVal.Y(), + tangDefVal.Z() + ]); + tangDefValues.push(tangDefVal); + } + thisEdge.edge_index = index; + + edgeList.push(thisEdge); + tangDefValues.forEach(v => v.delete()); + aLocation.delete(); + adaptorCurve.delete(); + tangDef.delete(); + this.occ.BRepTools.Clean(myEdge, true); + }); + + const vertices = this.shapeGettersService.getVertices({ shape: shapeToUse }); + if (vertices.length > 0) { + vertices.forEach(v => { + const pt = this.occ.BRep_Tool.Pnt(v); + pointsList.push([pt.X(), pt.Y(), pt.Z()]); + pt.delete(); + }); + } + + if (incrementalMeshBuilder) { + incrementalMeshBuilder.Delete(); + } + this.occ.BRepTools.Clean(shapeToUse, true); + return { faceList, edgeList, pointsList }; + } + + meshMeshIntersectionWires(inputs: Inputs.OCCT.MeshMeshIntersectionTwoShapesDto): TopoDS_Wire[] { + const shape1 = inputs.shape1; + const shape2 = inputs.shape2; + + const mesh1 = this.shapeFacesToPolygonPoints({ shape: shape1, precision: inputs.precision1, adjustYtoZ: false, reversedPoints: false }) as Inputs.Base.Mesh3; + const mesh2 = this.shapeFacesToPolygonPoints({ shape: shape2, precision: inputs.precision2, adjustYtoZ: false, reversedPoints: false }) as Inputs.Base.Mesh3; + + const res = this.mesh.meshMeshIntersectionPolylines({ + mesh1, mesh2 + }); + const wires = []; + res.forEach(r => { + if (r.points && r.points.length > 0) { + if (r.isClosed) { + wires.push( + this.wiresService.createPolygonWire({ + points: r.points, + })); + } else { + wires.push( + this.wiresService.createPolylineWire({ + points: r.points, + }) + ); + } + } + }); + return wires; + } + + meshMeshIntersectionPoints(inputs: Inputs.OCCT.MeshMeshIntersectionTwoShapesDto): Inputs.Base.Point3[][] { + const shape1 = inputs.shape1; + const shape2 = inputs.shape2; + + const mesh1 = this.shapeFacesToPolygonPoints({ shape: shape1, precision: inputs.precision1, adjustYtoZ: false, reversedPoints: false }) as Inputs.Base.Mesh3; + const mesh2 = this.shapeFacesToPolygonPoints({ shape: shape2, precision: inputs.precision2, adjustYtoZ: false, reversedPoints: false }) as Inputs.Base.Mesh3; + + return this.mesh.meshMeshIntersectionPoints({ mesh1, mesh2 }); + } + + meshMeshIntersectionOfShapesWires(inputs: Inputs.OCCT.MeshMeshesIntersectionOfShapesDto): TopoDS_Wire[] { + const wireIntersections: TopoDS_Wire[] = []; + + inputs.shapes.forEach((shape, index) => { + const shape1 = inputs.shape; + const shape2 = inputs.shapes[index]; + let precision2 = inputs.precision; + if (inputs.precisionShapes && inputs.precisionShapes.length > 0) { + const p = inputs.precisionShapes[index]; + if (p) { + precision2 = p; + } + } + + const wires = this.meshMeshIntersectionWires({ shape1, shape2, precision1: inputs.precision, precision2 }); + wireIntersections.push(...wires); + }); + return wireIntersections; + } + + meshMeshIntersectionOfShapesPoints(inputs: Inputs.OCCT.MeshMeshesIntersectionOfShapesDto): Inputs.Base.Point3[][] { + const pointIntersections: Inputs.Base.Point3[][] = []; + + inputs.shapes.forEach((shape, index) => { + const shape1 = inputs.shape; + const shape2 = inputs.shapes[index]; + let precision2 = inputs.precision; + if (inputs.precisionShapes && inputs.precisionShapes.length > 0) { + const p = inputs.precisionShapes[index]; + if (p) { + precision2 = p; + } + } + + const points = this.meshMeshIntersectionPoints({ shape1, shape2, precision1: inputs.precision, precision2 }); + pointIntersections.push(...points); + }); + return pointIntersections; + } + +} diff --git a/packages/dev/occt/lib/services/base/vertices.service.ts b/packages/dev/occt/lib/services/base/vertices.service.ts index 5da62f69..095b0511 100644 --- a/packages/dev/occt/lib/services/base/vertices.service.ts +++ b/packages/dev/occt/lib/services/base/vertices.service.ts @@ -14,7 +14,7 @@ export class VerticesService { private readonly converterService: ConverterService, private readonly shapeGettersService: ShapeGettersService, public wiresService: WiresService, - private readonly booleansService: BooleansService, + public booleansService: BooleansService, ) { } diff --git a/packages/dev/occt/lib/services/base/wires.service.ts b/packages/dev/occt/lib/services/base/wires.service.ts index b1901591..8941b9a3 100644 --- a/packages/dev/occt/lib/services/base/wires.service.ts +++ b/packages/dev/occt/lib/services/base/wires.service.ts @@ -13,10 +13,10 @@ import { GeomService } from "./geom.service"; import { TransformsService } from "./transforms.service"; import { ConverterService } from "./converter.service"; import { EnumService } from "./enum.service"; -import { TextBitByBit, Vector } from "@bitbybit-dev/base"; +import { Point, TextBitByBit, Vector } from "@bitbybit-dev/base"; import { TextWiresDataDto, ObjectDefinition } from "../../api/models/bucket"; import { OperationsService } from "./operations.service"; -import { ShapeParser } from "../../shape-parser"; +import { FilletsService } from "./fillets.service"; export class WiresService { @@ -24,6 +24,7 @@ export class WiresService { private readonly occ: OpenCascadeInstance, private readonly occRefReturns: OCCReferencedReturns, private readonly vector: Vector, + private readonly point: Point, private readonly shapesHelperService: ShapesHelperService, private readonly shapeGettersService: ShapeGettersService, private readonly transformsService: TransformsService, @@ -33,6 +34,7 @@ export class WiresService { private readonly geomService: GeomService, private readonly edgesService: EdgesService, private readonly textService: TextBitByBit, + public filletsService: FilletsService, public operationsService: OperationsService, ) { } @@ -748,6 +750,99 @@ export class WiresService { return this.geomService.getLinearCenterOfMass(inputs); } + hexagonsInGrid(inputs: Inputs.OCCT.HexagonsInGridDto): TopoDS_Wire[] { + const hex = this.point.hexGridScaledToFit({ ...inputs, centerGrid: true, pointsOnGround: true }); + const wires = hex.hexagons.map(hex => { + return this.createPolygonWire({ points: hex }); + }); + + let currentScalePatternWidthIndex = 0; + let currentScalePatternHeightIndex = 0; + let currentInclusionPatternIndex = 0; + let currentFilletPatternIndex = 0; + + const res = []; + + for (let i = 0; i < inputs.nrHexagonsInHeight; i++) { + for (let j = 0; j < inputs.nrHexagonsInWidth; j++) { + + let scaleFromPatternWidth = 1; + if (inputs.scalePatternWidth && inputs.scalePatternWidth.length > 0) { + scaleFromPatternWidth = inputs.scalePatternWidth[currentScalePatternWidthIndex]; + currentScalePatternWidthIndex++; + if (currentScalePatternWidthIndex >= inputs.scalePatternWidth.length) { + currentScalePatternWidthIndex = 0; + } + } + + let scaleFromPatternHeight = 1; + if (inputs.scalePatternHeight && inputs.scalePatternHeight.length > 0) { + scaleFromPatternHeight = inputs.scalePatternHeight[currentScalePatternHeightIndex]; + currentScalePatternHeightIndex++; + if (currentScalePatternHeightIndex >= inputs.scalePatternHeight.length) { + currentScalePatternHeightIndex = 0; + } + } + let include = true; + if (inputs.inclusionPattern && inputs.inclusionPattern.length > 0) { + include = inputs.inclusionPattern[currentInclusionPatternIndex]; + currentInclusionPatternIndex++; + if (currentInclusionPatternIndex >= inputs.inclusionPattern.length) { + currentInclusionPatternIndex = 0; + } + } + + let fillet = 0; + if (inputs.filletPattern && inputs.filletPattern.length > 0) { + fillet = inputs.filletPattern[currentFilletPatternIndex]; + currentFilletPatternIndex++; + if (currentFilletPatternIndex >= inputs.filletPattern.length) { + currentFilletPatternIndex = 0; + } + } + + if (include) { + fillet = hex.maxFilletRadius * fillet; + + const hexagon = wires[i * inputs.nrHexagonsInWidth + j]; + const hexagonCenter = hex.centers[i * inputs.nrHexagonsInWidth + j]; + + if (fillet > 0) { + const filletRectangle = this.filletsService.fillet2d({ + shape: hexagon, + radius: fillet, + }); + + const scaleVec2 = [scaleFromPatternWidth, 1, scaleFromPatternHeight] as Base.Vector3; + let hexScaled = filletRectangle; + if (scaleFromPatternWidth !== 1 || scaleFromPatternHeight !== 1) { + hexScaled = this.transformsService.scale3d({ + shape: filletRectangle, + center: hexagonCenter, + scale: scaleVec2, + }); + } + + res.push(hexScaled); + } else { + const scaleVec2 = [scaleFromPatternWidth, 1, scaleFromPatternHeight] as Base.Vector3; + let hexScaled = hexagon; + if (scaleFromPatternWidth !== 1 || scaleFromPatternHeight !== 1) { + hexScaled = this.transformsService.scale3d({ + shape: hexagon, + center: hexagonCenter, + scale: scaleVec2, + }); + } + + res.push(hexScaled); + } + } + } + } + return res; + } + createWireFromEdge(inputs: Inputs.OCCT.ShapeDto): TopoDS_Wire { const makeWire = new this.occ.BRepBuilderAPI_MakeWire_2(inputs.shape); const wire = makeWire.Wire(); diff --git a/packages/dev/occt/lib/services/booleans.ts b/packages/dev/occt/lib/services/booleans.ts index bd34d059..0242ceb8 100644 --- a/packages/dev/occt/lib/services/booleans.ts +++ b/packages/dev/occt/lib/services/booleans.ts @@ -1,4 +1,4 @@ -import { OpenCascadeInstance, TopoDS_Shape } from "../../bitbybit-dev-occt/bitbybit-dev-occt"; +import { OpenCascadeInstance, TopoDS_Shape,TopoDS_Wire } from "../../bitbybit-dev-occt/bitbybit-dev-occt"; import { OccHelper } from "../occ-helper"; import * as Inputs from "../api/inputs/inputs"; @@ -24,4 +24,22 @@ export class OCCTBooleans { return res; } + meshMeshIntersectionWires(inputs: Inputs.OCCT.MeshMeshIntersectionTwoShapesDto): TopoDS_Wire[] { + return this.och.meshingService.meshMeshIntersectionWires(inputs); + } + + meshMeshIntersectionPoints(inputs: Inputs.OCCT.MeshMeshIntersectionTwoShapesDto): Inputs.Base.Point3[][] { + return this.och.meshingService.meshMeshIntersectionPoints(inputs); + } + + meshMeshIntersectionOfShapesWires(inputs: Inputs.OCCT.MeshMeshesIntersectionOfShapesDto): TopoDS_Wire[] { + return this.och.meshingService.meshMeshIntersectionOfShapesWires(inputs); + } + + meshMeshIntersectionOfShapesPoints(inputs: Inputs.OCCT.MeshMeshesIntersectionOfShapesDto): Inputs.Base.Point3[][] { + return this.och.meshingService.meshMeshIntersectionOfShapesPoints(inputs); + } + + + } diff --git a/packages/dev/occt/lib/services/shapes/face.ts b/packages/dev/occt/lib/services/shapes/face.ts index e0e0743f..e61282ee 100644 --- a/packages/dev/occt/lib/services/shapes/face.ts +++ b/packages/dev/occt/lib/services/shapes/face.ts @@ -35,6 +35,7 @@ export class OCCTFace { return this.och.facesService.createFaceFromWiresOnFace(inputs); } + faceFromSurface(inputs: Inputs.OCCT.ShapeWithToleranceDto) { return this.och.facesService.faceFromSurface(inputs); } @@ -79,6 +80,14 @@ export class OCCTFace { return this.och.facesService.subdivideToRectangleHoles(inputs); } + subdivideToHexagonWires(inputs: Inputs.OCCT.FaceSubdivideToHexagonWiresDto): TopoDS_Wire[] { + return this.och.facesService.subdivideToHexagonWires(inputs); + } + + subdivideToHexagonHoles(inputs: Inputs.OCCT.FaceSubdivideToHexagonHolesDto): TopoDS_Face[] { + return this.och.facesService.subdivideToHexagonHoles(inputs); + } + subdivideToNormals(inputs: Inputs.OCCT.FaceSubdivisionDto): Base.Point3[] { return this.och.facesService.subdivideToNormals(inputs); } @@ -131,6 +140,11 @@ export class OCCTFace { return this.och.entitiesService.createCircle(inputs.radius, inputs.center, inputs.direction, Inputs.OCCT.typeSpecificityEnum.face) as TopoDS_Face; } + hexagonsInGrid(inputs: Inputs.OCCT.HexagonsInGridDto): TopoDS_Face[] { + const hexagonWires = this.och.wiresService.hexagonsInGrid(inputs); + return this.och.facesService.createFacesFromWires({ shapes: hexagonWires, planar: true }); + } + createEllipseFace(inputs: Inputs.OCCT.EllipseDto): TopoDS_Face { return this.och.entitiesService.createEllipse(inputs.radiusMinor, inputs.radiusMajor, inputs.center, inputs.direction, Inputs.OCCT.typeSpecificityEnum.face) as TopoDS_Face; } diff --git a/packages/dev/occt/lib/services/shapes/wire.ts b/packages/dev/occt/lib/services/shapes/wire.ts index f6d5023b..9b01cbf3 100644 --- a/packages/dev/occt/lib/services/shapes/wire.ts +++ b/packages/dev/occt/lib/services/shapes/wire.ts @@ -88,6 +88,10 @@ export class OCCTWire { return this.och.converterService.makeCompoundIfNeeded(wires, inputs.returnCompound); } + hexagonsInGrid(inputs: Inputs.OCCT.HexagonsInGridDto): TopoDS_Wire[] { + return this.och.wiresService.hexagonsInGrid(inputs); + } + createZigZagBetweenTwoWires(inputs: Inputs.OCCT.ZigZagBetweenTwoWiresDto): TopoDS_Wire { return this.och.wiresService.createZigZagBetweenTwoWires(inputs); } diff --git a/packages/dev/occt/package-lock.json b/packages/dev/occt/package-lock.json index 9bf30e46..5779a1b8 100644 --- a/packages/dev/occt/package-lock.json +++ b/packages/dev/occt/package-lock.json @@ -1,15 +1,15 @@ { "name": "@bitbybit-dev/occt", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/occt", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" }, "devDependencies": { "@babel/core": "7.16.0", @@ -1713,9 +1713,9 @@ "dev": true }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", @@ -9489,9 +9489,9 @@ "dev": true }, "@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "@cspotcode/source-map-support": { "version": "0.8.1", diff --git a/packages/dev/occt/package.json b/packages/dev/occt/package.json index 2cc1afce..3b13b9de 100644 --- a/packages/dev/occt/package.json +++ b/packages/dev/occt/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/occt", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers CAD algorithms using OpenCascade Technology kernel. Run in Node and in Browser.", "main": "index.js", "repository": { @@ -35,7 +35,7 @@ "types": "./index.d.ts", "type": "module", "dependencies": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" }, "devDependencies": { "sass": "1.57.1", diff --git a/packages/dev/threejs/lib/api/bitbybit-base.ts b/packages/dev/threejs/lib/api/bitbybit-base.ts index 56a34418..a53a7268 100644 --- a/packages/dev/threejs/lib/api/bitbybit-base.ts +++ b/packages/dev/threejs/lib/api/bitbybit-base.ts @@ -73,10 +73,11 @@ export class BitByBitBase { this.tag = new Tag(this.context); this.draw = new Draw(drawHelper, this.context, this.tag); this.color = new Color(this.math); - this.line = new Line(this.point, geometryHelper); + this.line = new Line(this.vector, this.point, geometryHelper); this.transforms = new Transforms(this.vector, this.math); - this.point = new Point(geometryHelper, this.transforms, this.vector); - this.polyline = new Polyline(this.vector, this.point, geometryHelper); + this.lists = new Lists(); + this.point = new Point(geometryHelper, this.transforms, this.vector, this.lists); + this.polyline = new Polyline(this.vector, this.point, this.line, geometryHelper); this.verb = new Verb(this.context, geometryHelper, this.math); this.time = new Time(this.context); this.occt = new OCCTW(this.context, this.occtWorkerManager); @@ -85,7 +86,6 @@ export class BitByBitBase { this.json = new JSONBitByBit(this.context); this.text = new TextBitByBit(this.point); this.dates = new Dates(); - this.lists = new Lists(); this.mesh = new MeshBitByBit(this.vector, this.polyline); } diff --git a/packages/dev/threejs/package-lock.json b/packages/dev/threejs/package-lock.json index cb80228c..143909c7 100644 --- a/packages/dev/threejs/package-lock.json +++ b/packages/dev/threejs/package-lock.json @@ -1,15 +1,15 @@ { "name": "@bitbybit-dev/threejs", - "version": "0.20.2", + "version": "0.20.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitbybit-dev/threejs", - "version": "0.20.2", + "version": "0.20.3", "license": "MIT", "dependencies": { - "@bitbybit-dev/core": "0.20.2", + "@bitbybit-dev/core": "0.20.3", "three": "0.176.0" }, "devDependencies": { @@ -1703,30 +1703,30 @@ "dev": true }, "node_modules/@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "node_modules/@bitbybit-dev/core": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.2.tgz", - "integrity": "sha512-IYa7ThvCgOMS9CoRvuwTJ/YhAyCVZ0gamXmtb9dYF33RuhtRDWc6nxgitXQ/WBUQI/Csk7hzXlJS32nChMqCNA==", - "dependencies": { - "@bitbybit-dev/base": "0.20.2", - "@bitbybit-dev/jscad-worker": "0.20.2", - "@bitbybit-dev/manifold-worker": "0.20.2", - "@bitbybit-dev/occt-worker": "0.20.2", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.3.tgz", + "integrity": "sha512-SVSHyvysBJGK80YBUf/W0AAqc2561svpE6xFORnyq7i8PrBjWnKPgWHRn138lUloff4aS40s6QFCqerN3IFM7Q==", + "dependencies": { + "@bitbybit-dev/base": "0.20.3", + "@bitbybit-dev/jscad-worker": "0.20.3", + "@bitbybit-dev/manifold-worker": "0.20.3", + "@bitbybit-dev/occt-worker": "0.20.3", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "node_modules/@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "dependencies": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -1735,45 +1735,45 @@ } }, "node_modules/@bitbybit-dev/jscad-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.2.tgz", - "integrity": "sha512-bTnlO3yqvTNFqAx3dGnhrX/O7HPNuXRE5vn68ogK7belWEI8r26XaN5kJEzcN8k9maxAXJQRR04xJQPkbfGTaA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.3.tgz", + "integrity": "sha512-V1B7FPvpyDbDjmlunaaCSUo2kkMT164YaDoQMRNGjxknLI+lzymz13W7J99VZrlekJY2RSaMDaYx+9k0ELXbtw==", "dependencies": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "dependencies": { "manifold-3d": "3.0.0" } }, "node_modules/@bitbybit-dev/manifold-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.2.tgz", - "integrity": "sha512-+Zid8ALBXGTSLn6DlhU8abHduu5HlUpFZ5+RoIshXi4z5xQJbVhEWckDn1PF2Uq9y4qnwHTMQK+etF40Aei2MQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.3.tgz", + "integrity": "sha512-EWN8k6yXK1XWXK/Km4Jx3xB9H3joEi1XJo48OU8CQHRMvplpMt0P5LmqiskyT9Y5Gm16YLOVm/8AX6f4Vra9vQ==", "dependencies": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" } }, "node_modules/@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "dependencies": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "node_modules/@bitbybit-dev/occt-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.2.tgz", - "integrity": "sha512-q6h/PbBAKnp01PshmhGE14x2b7YnW02YvnUrdGkU6FVYErxoQwwASiN10UKgeBbkD+jbc099kpFzJtvamPnMRw==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.3.tgz", + "integrity": "sha512-2fDVkW42TPzrAswTCX9jWU96L2TkTGssIb82iX+ORnv8vpUb/P7Ih5vMsUUkHLZmFyft61dTvPDSjN1bqPAgQw==", "dependencies": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" } }, @@ -8451,30 +8451,30 @@ "dev": true }, "@bitbybit-dev/base": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.2.tgz", - "integrity": "sha512-+8jxnr7n7SNnuvh3uBgFQSTFYlOA+UrXUhE5XpeukPtTt3ffRShfTC3eRP9q+m/ummljKpKVLGGwSlCsKbriBQ==" + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/base/-/base-0.20.3.tgz", + "integrity": "sha512-uerxybsWRCd+3BnhBvonlPO6jyBVxcA5ULQes6JnaZ/oaL0FSZ+lms83ZZF+jrra/kK1lgfxr6wRI8bzqQynAg==" }, "@bitbybit-dev/core": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.2.tgz", - "integrity": "sha512-IYa7ThvCgOMS9CoRvuwTJ/YhAyCVZ0gamXmtb9dYF33RuhtRDWc6nxgitXQ/WBUQI/Csk7hzXlJS32nChMqCNA==", - "requires": { - "@bitbybit-dev/base": "0.20.2", - "@bitbybit-dev/jscad-worker": "0.20.2", - "@bitbybit-dev/manifold-worker": "0.20.2", - "@bitbybit-dev/occt-worker": "0.20.2", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/core/-/core-0.20.3.tgz", + "integrity": "sha512-SVSHyvysBJGK80YBUf/W0AAqc2561svpE6xFORnyq7i8PrBjWnKPgWHRn138lUloff4aS40s6QFCqerN3IFM7Q==", + "requires": { + "@bitbybit-dev/base": "0.20.3", + "@bitbybit-dev/jscad-worker": "0.20.3", + "@bitbybit-dev/manifold-worker": "0.20.3", + "@bitbybit-dev/occt-worker": "0.20.3", "jsonpath-plus": "10.1.0", "rxjs": "7.5.5", "verb-nurbs-web": "2.1.3" } }, "@bitbybit-dev/jscad": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.2.tgz", - "integrity": "sha512-jRkvkjQM9OwtuB6grNndK4io28woHSiDULfYkEckdK7Ms13esj0qtsi5j1NaAZizd04xdKVuThFhEzIzRkdkkQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad/-/jscad-0.20.3.tgz", + "integrity": "sha512-LMd77pN3Wzq3piP9TlhSpBHqPuPerjv68OibCs1MDLSPN5PgYx5OnnW23iuIpv4Cfn4kgw3pG4kZDDUlsX4cXA==", "requires": { - "@bitbybit-dev/base": "0.20.2", + "@bitbybit-dev/base": "0.20.3", "@jscad/3mf-serializer": "2.1.12", "@jscad/dxf-serializer": "2.1.18", "@jscad/io-utils": "2.0.28", @@ -8483,45 +8483,45 @@ } }, "@bitbybit-dev/jscad-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.2.tgz", - "integrity": "sha512-bTnlO3yqvTNFqAx3dGnhrX/O7HPNuXRE5vn68ogK7belWEI8r26XaN5kJEzcN8k9maxAXJQRR04xJQPkbfGTaA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/jscad-worker/-/jscad-worker-0.20.3.tgz", + "integrity": "sha512-V1B7FPvpyDbDjmlunaaCSUo2kkMT164YaDoQMRNGjxknLI+lzymz13W7J99VZrlekJY2RSaMDaYx+9k0ELXbtw==", "requires": { - "@bitbybit-dev/jscad": "0.20.2", + "@bitbybit-dev/jscad": "0.20.3", "rxjs": "7.5.5" } }, "@bitbybit-dev/manifold": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.2.tgz", - "integrity": "sha512-BJtFUzC2Juxm/NYYyOvu/TiXccvzSvb5dOomrTE9uHv04TPCwxy3sw/mMU1QeoJyeQ4F2eIaG0GgXUe8fCsHYA==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold/-/manifold-0.20.3.tgz", + "integrity": "sha512-Hf7Xqevh2JqjMlkedr08pAmv8L9YSuK5k3kJZ/obg2eqxpKj3ttnzlwS/WeIDk59+i2sHjSOR1XqiHZ65s0CIA==", "requires": { "manifold-3d": "3.0.0" } }, "@bitbybit-dev/manifold-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.2.tgz", - "integrity": "sha512-+Zid8ALBXGTSLn6DlhU8abHduu5HlUpFZ5+RoIshXi4z5xQJbVhEWckDn1PF2Uq9y4qnwHTMQK+etF40Aei2MQ==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/manifold-worker/-/manifold-worker-0.20.3.tgz", + "integrity": "sha512-EWN8k6yXK1XWXK/Km4Jx3xB9H3joEi1XJo48OU8CQHRMvplpMt0P5LmqiskyT9Y5Gm16YLOVm/8AX6f4Vra9vQ==", "requires": { - "@bitbybit-dev/manifold": "0.20.2", + "@bitbybit-dev/manifold": "0.20.3", "rxjs": "7.5.5" } }, "@bitbybit-dev/occt": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.2.tgz", - "integrity": "sha512-jPZyi50Z2BjCSJ+OwlroEG3Neqd8uPDtqp8skhREDSPxG6SAjIHCxxn7SZxl8w7rhhUDR/OCGm/uDdLeLR7Ncg==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt/-/occt-0.20.3.tgz", + "integrity": "sha512-hpdmS3PtE6gAiUPMT6fsw2LcJsU6eJle9bhXW1zpXLY4+aSXoUc1pv92Oh631YeRC9TUPbidBDJEzIQMODciig==", "requires": { - "@bitbybit-dev/base": "0.20.2" + "@bitbybit-dev/base": "0.20.3" } }, "@bitbybit-dev/occt-worker": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.2.tgz", - "integrity": "sha512-q6h/PbBAKnp01PshmhGE14x2b7YnW02YvnUrdGkU6FVYErxoQwwASiN10UKgeBbkD+jbc099kpFzJtvamPnMRw==", + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@bitbybit-dev/occt-worker/-/occt-worker-0.20.3.tgz", + "integrity": "sha512-2fDVkW42TPzrAswTCX9jWU96L2TkTGssIb82iX+ORnv8vpUb/P7Ih5vMsUUkHLZmFyft61dTvPDSjN1bqPAgQw==", "requires": { - "@bitbybit-dev/occt": "0.20.2", + "@bitbybit-dev/occt": "0.20.3", "rxjs": "7.5.5" } }, diff --git a/packages/dev/threejs/package.json b/packages/dev/threejs/package.json index 44fb2326..0f4f09b5 100644 --- a/packages/dev/threejs/package.json +++ b/packages/dev/threejs/package.json @@ -1,6 +1,6 @@ { "name": "@bitbybit-dev/threejs", - "version": "0.20.2", + "version": "0.20.3", "description": "Bit By Bit Developers THREEJS CAD Library to Program Geometry", "main": "index.js", "repository": { @@ -55,7 +55,7 @@ "type": "module", "dependencies": { "three": "0.176.0", - "@bitbybit-dev/core": "0.20.2" + "@bitbybit-dev/core": "0.20.3" }, "devDependencies": { "sass": "1.57.1",