Three.js: التبعيات الدورية

تم إنشاؤها على ١٦ مارس ٢٠١٥  ·  81تعليقات  ·  مصدر: mrdoob/three.js

مرحبا جميعا.

لقد كنت أنا و kumavis في العمل بجد في محاولة لإيجاد طريقة فعالة لنقل THREE.js إلى بنية مستعرضات المتصفح. لقد أحرزنا تقدمًا جيدًا ، حتى لدرجة نقل جميع الملفات إلى نظام بناء المتصفح والقدرة على إنشاء ملف three.min.js باستخدام gulp.

لسوء الحظ ، الأمثلة لا تعمل ، لأنه على عكس Commonjs browserify لا يمكنه التعامل مع التبعيات الدورية ، والتي يوجد الكثير منها في THREE.js.

لقد قمت بعمل رسم بياني تفاعلي يصور علاقات التبعية هنا .

ما لم يتم حل هذه التشابكات وحتى يتم حلها ، لن نتمكن من نقل THREE.js إلى إصدار browserify.

لا أعتبر هذا نقصًا في browserify ، ولكنه يمثل مشكلة مع THREE.js. التبعيات الدائرية أمر سيء في البرامج بشكل عام ، وتؤدي إلى جميع أنواع المشاكل.

Suggestion

التعليق الأكثر فائدة

@ Mugen87 نجاح باهر ، 5 سنوات! تهانينا على ضربه أخيرًا: fire:: clap:
لقد استمتعت كثيرًا بعمل تلك الرسوم البيانية في ذلك الوقت: smile_cat:

ال 81 كومينتر

هذه عقدة كبيرة لفك التشابك
http://jsbin.com/medezu/2/edit؟html ، js، output
image

coballast هل يمكنك نشر الرمز الذي استخدمته لإنشاء التبعية json؟

ما عليك سوى استخدام ملف three.min.js المترجم مسبقًا مباشرةً. ليست هناك حاجة لتقسيم Three.js إلى ملفات فردية داخل Browserfy ، فأنت تجعل حياتك أكثر صعوبة بدون فائدة حقيقية.

أتحدث من التجربة ، من حيث أننا نستخدم وحدة npm المكونة من three.js وهي تعمل بشكل رائع. نحن فقط نجمعه كملف واحد ونلفه في وحدة نمط CommonJS. سيعمل هذا النهج مع browserfy والكثير من الناس يفعلون ذلك بالفعل وأنا أفهم.

فك هذه العقدة ليس ضروريًا لحالة الاستخدام هذه.

kumavis لقد تخلصت للتو من هيكل التبعية. ثم قام الكود التالي بإنشاء الرسم البياني:

var fs = require('fs-extra');
var unique = require('uniq');
var util = require('util');

function getRequiredObjects(dependencies){
  var result = [];
  for(var i = 0; i < dependencies.usedObjects.length; i++){
    var object = dependencies.usedObjects[i];
    if(dependencies.definedObjects.indexOf(object) == -1)
      result.push(object);
  }

  return result;
};

var dependencies = JSON.parse(fs.readFileSync('./dependencies.json'));

var objects = [];
for(var f in dependencies){
  objects = objects.concat(dependencies[f].usedObjects);
  objects = objects.concat(dependencies[f].definedObjects);
}

objects = unique(objects);


var nodes = objects.map(function(o){
  return {data: {id: o} };
});

var edges = [];
for(var f in dependencies){
  var dependency = dependencies[f];
  var requiredObjects = getRequiredObjects(dependency);
  for(var j = 0; j < dependency.definedObjects.length; j++){
    for(var k = 0; k < requiredObjects.length; k++){
      edges.push({ data: { source: dependency.definedObjects[j], target: requiredObjects[k] } });
    }
  }
}

var graph = {nodes: nodes, edges: edges};

var eliminateImpossibleCycleNodes = function(graph){
  graph.nodes = graph.nodes.filter(function(node){
    var source_edge = null;
    var dest_edge = null;
    for(var i = 0; i < graph.edges.length; i++){
      if(graph.edges[i].data.source == node.data.id)
        source_edge = graph.edges[i];
      if(graph.edges[i].data.target == node.data.id)
        dest_edge = graph.edges[i];
    }

    if(source_edge != null && dest_edge != null)
      return true;
    else
      return false;
  });

  graph.edges = graph.edges.filter(function(edge){
    var source_exists = false, target_exists = false;
    for(var i = 0; i < graph.nodes.length; i++){
      if(edge.data.source == graph.nodes[i].data.id)
        source_exists = true;
      if(edge.data.target == graph.nodes[i].data.id)
        target_exists = true;
    }

    return source_exists && target_exists;
  });
};

for(var i = 0; i < 500; i++)
  eliminateImpossibleCycleNodes(graph)


console.log(JSON.stringify(graph));

bhouston ، يتعلق الأمر أكثر بصحة قاعدة الرموز three.js

أنا أعلم فقط أنه في مكتبة الرياضيات ، التي ساعدتُ فيها قليلًا ، الاعتمادات الدورية هي القاعدة في جميع اللغات. لأن الدوال في Matrix4 قد تأخذ Vector3 كمعلمات ، وقد يكون Vector3 قادرًا على التحويل بواسطة Matrix4. لجعل جميع التبعيات بطريقة واحدة في مكتبة الرياضيات ، فإن هذا الجزء من المكتبة مزعج للاستخدام.

الآن أنا أدافع عن أن مكتبة الرياضيات لا تعرف أي جزء آخر من المكتبة - يجب ألا تتسرب الأنواع الأكثر تعقيدًا إلى هذه الوحدة. وبهذا المعنى ، فإنني أؤيد محاولة تقليل التبعيات الدورية بين الوحدات ، ولكن ليس إزالة جميع التبعيات الدورية بين الملفات الفردية داخل الوحدة النمطية.

هذه حالة توضح المضاعفات الدقيقة. لأكون واضحًا ، أنا هنا لست منتقدًا للتنفيذ نفسه ، ولكن الآثار الجانبية.

يشكل Vector3 و Matrix4 تبعية دورية لأنهما يعرضان نطاقًا من الوظائف التي تستخدم بعضها البعض كأنواع إدخال أو إخراج. يتم تنفيذ كلاهما بأسلوب مشترك في Three.js ، وتحديد الوظائف عبر IIFEs لتضمين متغيرات الخدش لإجراء العمليات الحسابية.

Matrix4#lookAt قادر على إنشاء نقطة الصفر فورًا ، كجزء من تعريف الوظيفة.

lookAt: function () {

  var x = new THREE.Vector3();
  var y = new THREE.Vector3();
  var z = new THREE.Vector3();

  return function ( eye, target, up ) {
    /* ... */

ومع ذلك ، يجب أن يقوم Vector3#project بإنشاء مثيل للخدش عند أول تشغيل.

project: function () {

  var matrix;

  return function ( camera ) {

    if ( matrix === undefined ) matrix = new THREE.Matrix4();

    /* ... */

لماذا ا؟ لأنه عند تحديد الفئة ، لم يتم تحديد جميع الفئات بعد. عند تحديد Vector3 ، لم يكن Matrix4 موجودًا بعد. الآن الوقت الفعلي لإنشاء مثيل لمتغيرات التسويد ليس مهمًا حقًا. الخلاصة الحقيقية هنا هي أن التنفيذ الحالي يتوقف على الترتيب الذي يربط نظام الإنشاء الملفات معًا. هذا اقتران بعيد حقًا ، والتغييرات في نظام الإنشاء أو إعادة تسمية الملفات بطريقة تغير ترتيب السلسلة يمكن أن يؤدي إلى إنشاءات غير صالحة ، بدون اتصال واضح.

هذه فقط إحدى الطرق التي تظهر بها هذه العقدة في شكل أخطاء. ومع ذلك ، بينما يمكننا معالجة هذه المشكلة المحددة ، ليس لدي حل عام لا يتطلب الكثير من التغييرات الفاصلة في واجهة برمجة التطبيقات.

حسنًا ... لقد ألقيت نظرة على مكتبة الرياضيات C ++ الخاصة بـ ILM ، والتي أعتبرها المعيار الذهبي من حيث مكتبات الرياضيات. والمثير للدهشة أنها ليست دورية. لديهم أساسًا ترتيبًا محددًا جيدًا للأنواع البسيطة إلى المعقدة التي يحددونها وأعتقد أنه إذا قمت بذلك بعناية شديدة ، فإنه يعمل:

https://github.com/openexr/openexr/tree/master/IlmBase/Imath

يبدو أن الرياضيات ثم Vec هي الأبسط.

ملاحظات إضافية على الرسم البياني للتبعية:

يحتوي Material s على دفعات ثنائية الاتجاه مع صنفهم الأساسي
image
يصعب رؤيته قليلاً ، ولكن يبدو أن Geometry s يحتوي على أجزاء جيدة في اتجاه واحد في الفئة الأساسية
image
يوجد وضع مشابه لـ Light s و Camera s - جيد فيما يتعلق بفئتهم الأساسية ، لكن تبعية Object3D _ عليهم_ تبدو غير ضرورية.
image
image
يبدو أن Curve s Path s Line s يبدو جيدًا ، لكن Shape متشابك بعض الشيء.
image

coballast شكرا! هذه رؤية عظيمة.

أضف نفسي للتعليقات :)

راجع للشغل ، لقد بحثت في الطريقة التي تعتمد بها المواد على MeshDepthMaterial ، على سبيل المثال. إنه بسيط

if ( this instanceof THREE.MeshDepthMaterial )

وهو أمر تافه للتغيير إليه

if ( this.type == 'MeshDepthMaterial' )

وفويلا - لا تبعية. أعتقد أن نصف هذا الرسم البياني المخيف هو نفس مستوى المشكلة.

الشيء المضحك هو أن هذه التبعية تحدث بطريقة toJSON الفردية. أعني ، ألا يمكن استبداله فقط في MeshDepthMaterial بدلاً من ذلك؟ شيء مثل

THREE.MeshDepthMaterial.prototype.toJSON =  function () {
    var output = THREE.Material.prototype.toJSON.apply(this);
    if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending;
    if ( this.side !== THREE.FrontSide ) output.side = this.side;

makc بشكل عام في أي مكان نقوم به instanceof ، يجب أن ننقل هذا الرمز إلى الفئة المحددة نفسها. سيساعد ذلك على إزالة الكثير من العقد.

أردت فقط أن أقول بينما لا تدعم AMD المراجع الدائرية ، فإن وحدات ES6 تعمل https://github.com/ModuleLoader/es6-module-loader/wiki/Circular-References-&-Bindings

أنا فضولي فقط لأعرف بصرف النظر عن مشكلة حل التبعية (التي يمكن حلها في تنفيذ أداة تحميل نظام الوحدات على سبيل المثال system.js ) ، ما هي المشكلات التي تخلقها المراجع الدائرية في three.js؟

لحسن الحظ ، يبدو أنه يمكننا مهاجمة هذا على مراحل. أعتقد أنه تم إجراء الكثير من تغييرات كسر واجهة برمجة التطبيقات في الإصدارات السابقة ، لذلك لست متأكدًا من أن هذا غير وارد.

بالنسبة للحالات instanceof (ربما الأغلبية) ، يجب أن تكون قادرة على حلها بدون تغييرات فاصلة.

أنا أيضا أشترك هنا. دعنا نذهب خطوة بخطوة هنا.
أوافق على أنه يجب علينا إزالة جميع التبعيات الدورية غير الضرورية مثل تلك المادية.
أتفق أيضًا مع bhouston على أن مكتبة الرياضيات تعتمد بشكل كبير على بعضها البعض لأن التفاعل هو ما يجعل مكتبة الرياضيات مفيدة.

هل يمكن لشخص ما أن يرسم الأشياء السهلة ؟؟ دائمًا ما يكون وجود تبعيات دورية أقل فكرة جيدة إذا لم يعيق المكتبة. يمكننا لاحقًا أن نرى ما يجب أن نفعله مع الآخرين.

@ zz85 لقد واجهت أيضًا مشكلة التبعيات الدائرية. إنها في الغالب مشكلة عندما نحاول إنشاء كائنات معينة مسبقًا داخل ملفات مرجعية دائرية.

يجب أن يوضح 6252 الكثير من الإيداعات الدائرية على Material و Object3D .

هذا ما يبدو عليه Mesh . ربما بعض المستودعات الدخيلة ولكن ليست مجنونة للغاية.
image

تعميم Object3D و Geometry . تم تناول المرجع Object3D -> Mesh في العلاقات العامة أعلاه. المرجع Mesh -> Geometry جيد ، b / c Mesh يتحكم في مثيل Geometry . لا يزال من الممكن كسرها بسبب إجراء فحص النوع للسلوك الخاص بالفئة ( Geometry / BufferGeometry ).

أما المرجع Geometry -> Mesh ، فهو توفير geometry.mergeMesh( mesh ) . Geometry هو مفهوم ذو مستوى أدنى من Mesh ، لذلك سأعكسه ليصبح mesh.mergeIntoGeometry( geo ) وأوقف العمل mergeMesh .

إذا حصل شخص ما على علامة تجارية مدمجة تعمل على إصلاح بعض هذه المشكلات ، فيرجى إبلاغي بذلك وسوف أقوم بتحديث الرسم البياني ليعكس الوضع الحالي للأمور.

bhouston @ gero3 لست مقتنعًا بأن التبعيات الدورية مطلوبة للحصول على نفس المستوى من قابلية الاستخدام / المنفعة لمكتبة الرياضيات. قد أكون مخطئًا ، لكن لا يمكننا إبقاء Vector3 معزولًا تمامًا / جاهلًا ببقية النظام وتعديل نموذجه الأولي لاستيعاب Matrix4 في وحدة Matrix4؟ هذا منطقي بالنسبة لي من الناحية المفاهيمية ، لأن المصفوفات أكثر تعقيدًا من المتجهات. أعتقد أنه من الأفضل أن يكون لديك ترتيب محدد جيدًا يتم فيه إنشاء النماذج الأولية والفئات لمنع الحوادث المؤسفة.

bhouston @ gero3 أعتقد أننا قد نكون قادرين على القيام بذلك دون تغيير API على الإطلاق. سوف أتجول وأرى ماذا.

فيما يتعلق بالرياضيات ، يمكنك وضع كل "منصات الرسم" في مكان واحد ، على ما أعتقد. لكنني أراهن أنه لن يكون هناك رسم بياني واحد صالح للاستخدام لـ 3js لا يحتوي على كل من Vector3 و Matrix4

إذا كان هناك حل لا يغير الأداء أو واجهة برمجة التطبيقات لمكتبة الرياضيات ، فأنا أؤيده تمامًا.

coballast غير قادر على إزالة التوزيع الدوري بدون تغيير واجهة برمجة التطبيقات ، يوفر كل من b / c طرقًا تستخدم النوع الآخر. Vector3 Matrix4

بالنسبة إلى المتصفحات المتوافقة ، فإن مطلبنا الوحيد هو نقل إنشاء مثيل على متغيرات التسويد خارج وقت تعريف الفئة (اجعلها تتشكل عند التشغيل الأول). اجعل هذا كسول مثل هذا . لن يكون لذلك أي تأثير على API أو الأداء.

أعتقد أن هذه الأنواع من التغييرات لا بأس بها.

@ kumavis آه! نعم. حسنا أنا أفهم الآن. هذا سهل بما فيه الكفاية.

أؤيد تمامًا تقسيم ثلاث وحدات إلى وحدات أصغر ذات بنية تتطلب ، للأسباب التالية:

  1. ثلاثة كبير جدا. إذا كان المستخدم يحتاج فقط إلى ما يحتاج إليه ، فقد يقلل من أحجام بناء العميل. على سبيل المثال ، يتيح لك رد فعل التمهيد القيام بأشياء مثل var Alert = require('react-bootstrap/lib/Alert'); والتي لا تجمع كل وحدة تمهيدية.
  2. هناك بعض "المكونات الإضافية" ، مثل OrbitControls.js التي تعدل الكائن العام الثلاثة نفسه ، وتضع نفسها على THREE.OrbitControls . هذا مضاد للنمط في أطر عمل جافا سكريبت الحديثة لأنه يتطلب وجود ثلاثة أنماط في مساحة الاسم العالمية في عملية البناء ، بدلاً من أن تتطلبها الملفات التي تحتاجها. يقوم برنامج THREE أيضًا بهذا داخليًا ، حيث يقوم دائمًا بتعديل مساحة الاسم الثلاثة ، والتي لا تعد مثالية لتضمين ثلاث وحدات نمطية محددة.

وضع نفسه على ثلاثة

لكن كل جزء من الكود في 3js يفعل ذلك؟

DelvarWorld كتب:

أؤيد تمامًا تقسيم ثلاث وحدات إلى وحدات أصغر ذات بنية تتطلب ، للأسباب التالية:

كنت أعتقد أنه من الجيد تقسيمها ، ولكن هناك بساطة لـ ThreeJS كما هي الآن. إنه أكثر قابلية للاستخدام من قبل أولئك الجدد على 3D بالشكل الذي هو عليه الآن وكان ذلك من أولويات فريق التطوير. يمكنك استخدام ThreeJS دون الحاجة إلى نظام وحدة نمطية (يوجد الكثير منها ، وليست جميعها متوافقة تمامًا.)

مثل makc ، أشعر بالحيرة أيضًا بشأن اقتراح DelvarWorld بعدم وضع الأشياء في مساحة الأسماء الثلاثة.

أين / كيف سيكونون بدلاً من ذلك؟

بالنسبة لي ، يبدو أنه نمط جيد لإنشاء كائن عالمي واحد فقط ، ثلاثة ، تكون فيه جميع الأجزاء (وربما بعض الإضافات / الإضافات) منه.

أتفق مع DelvarWorld على أن أسلوب وضعه على المستوى العالمي ليس جيدًا لصحة قاعدة الرموز - إنها نوع من الحجة الدقيقة ب / ج وضعها على العالمية نفسها ليست هي المشكلة ، إنها الرسم البياني الخفي للتبعية ، والممارسات الأخرى التي تأتي من توافر العالمية.

لكن هذه الحجة تقتصر في الغالب على التطوير الداخلي وهيكل الكود. بالنسبة إلى تسليم المكتبة كحزمة أكواد ثابتة ، فإن وضع جميع الفئات على Global THREE أمر منطقي بالنسبة لي.

الحجة المضادة هي أنه عند إلغاء تسلسل مشهد json من THREE.js ، يمكن للإدخالات فقط سرد فئتها كسلسلة يمكن سحبها من العامة مثل: THREE[ obj.type ] . يعمل هذا مع الفئات غير الموجودة في three.js lib القياسي طالما أنك تحددها على THREE قبل إلغاء التسلسل. لست متأكدًا من أفضل طريقة لاستبدال هذا السلوك بدون THREE global.

يعمل هذا مع الفئات التي ليست في المستوى القياسي three.js lib طالما قمت بتعريفها في ثلاثة قبل إلغاء التسلسل. لست متأكدًا من أفضل السبل لاستبدال هذا السلوك بدون الثلاثية العالمية.

يمكنك عمل هذا النمط (أو بعض أشكاله) إذا كان كل شيء عبارة عن وحدة نمطية:

var objectType = require( "THREE." + obj.type );

هناك الكثير من التغييرات القادمة مع ES6 فيما يتعلق بالوحدات النمطية. كنت سأعيد النظر في نمطية ThreeJS في تلك المرحلة.

ستظل النسخة المبنية من ثلاثة (ملف جافا سكريبت الذي يمكن للأشخاص تنزيله يدويًا) تحتوي على كل شيء في مساحة الاسم الثلاثة. يمكنك القيام بذلك باستخدام ملف نقطة الدخول للبناء:

var THREE = {
    Geometry: require("./geometry"),

إلخ ، والتي ستظل مفيدة للوافدين الجدد ، ومن السهل البدء بها.

بالنسبة لأولئك الذين يستخدمون ثلاثة من npm و تتطلب js / browserify / webpack في بناء جافا سكريبت حديث ، يمكننا فعل شيء مثل

var Scene = require("three/scene"),
     Camera = require("three/camera"),

وما إلى ذلك ، مما قد يقلل من حجم ثلاثة مضمنة في حزمة حجم العميل. قد أكون مخطئا لأنني لا أعرف كم من ثلاثة هو "الأساسية". في الوقت الحالي ، على الرغم من ذلك ، هذا مستحيل لأن ثلاثة لا تستخدم عبارات تتطلب.

في كلتا الحالتين ، يتطلب النمط الحديث وحدات ، وبدلاً من إجراء جميع التعليمات البرمجية الخاصة بك لتعديل مكتبة أخرى (تعديل الثلاثية العالمية ، سيئة) ، فإن الكود الخاص بك مستقل وقياسي ، ويحدد ما يتطلبه ببيانات require ، مثل شفرة المصدر React .

لا أعتقد أن محاولتي تقديم حجة كاملة لاستخدام بناء جملة تتطلب / وحدة سيكون مفيدًا ، نظرًا لوجود العديد من الموارد الجيدة على الإنترنت حول هذا الموضوع. لكن من السيئ تشجيع الآخرين على تعديل ثلاثة أسماء من أجل إضافة مكونات إضافية مثل OrbitControls.

فقط كن على دراية بـDelvarWorld أن ES6 يقدم وحدات نمطية رسميًا في JavaScript ببنية محددة ومميزة للغاية:

http://www.2ality.com/2014/09/es6-modules-final.html

bhouston أوه نعم تمامًا ، أنا محايد لطلب مقابل الاستيراد (ربما يكون الاستيراد هو الخيار الأفضل) ، فقط دعم نمط الوحدة بشكل عام.

bhoustonDelvarWorldkumavis أحد مشاريعي طويلة المدى هو كتابة محول es5 -> es6 تلقائي يمكنه استيعاب وتحويل وحدات Commonjs / amd إلى es6 ، ونأمل في تحديد وإعادة كتابة الكثير من جافا سكريبت باستخدام بنيات es6 مثل الفئات / المولدات وما إلى ذلك وهلم جرا. يمكن للمرء أن يستخدم مستعرض تحويل مثل es6ify بينما المتصفحات تلحق بالمعيار لإعداد الكود للاستهلاك. يعد الانتقال إلى ثلاثة إلى المتصفح فقط على المستوى الداخلي خطوة أولى جيدة لإعداده للإدخال في مثل هذه الأداة.

هذا بجانب النقطة التي كنت (على ما يبدو سيئًا جدًا) أحاول توضيحها. أريد إزالة أكبر قدر ممكن من هذه التبعيات الدورية ، بغض النظر عن أي مخاوف تتعلق بالنمطية ، لأنني أعتقد أنها ستجعل ثلاثة أكثر استقرارًا ومرونة ، وربما تقضي على الكثير من الأخطاء كأثر جانبي لطيف.

تم دمج coballast https://github.com/mrdoob/three.js/pull/6252 ، يجب تقليل الكثير من الأقسام الدورية. هل تعتقد أنه يمكنك إنشاء رسم بياني جديد للفرع؟ ربما تجعله أداة مساعدة في الريبو لأداة التحويل

التالي هو: عمل أدوات الخدش في Vector3 Matrix4 معرّفة بتكاسل عند الاستخدام الأول ، وليس في وقت التعريف

أي شخص يريد التطوع؟ يجب أن تكون سريعة

تم تحديث الرسم البياني. http://jsbin.com/medezu/3/

هنا هي لقطة الشاشة:

snapshot3

يسعدني أن أبلغكم أنه تم القضاء على عدد كبير من دوائر Object3Ds. عمل جيد @ kumavis!

واو هذا هو نفس مصدر الشفرة؟ مجنون

سيعمل على جعل إنشاء الرسم البياني جزءًا من الأداة.

عند فحص الرسم البياني وحده ، يبدو أن Shape و Geometry من الممكن أن يكونا شجرتين أصليتين يمكن فكهما.

coballast تعتقد أنه يمكنك تحمل هذا؟

جعل أدوات الخدش في Vector3 Matrix4 محددة بشكل كسول عند الاستخدام الأول ، وليس في وقت التعريف

سيكون ذلك بمثابة PR مقابل dev في مقابل التغيير الآلي

أعتقد أيضًا أنه يمكننا تخفيض عنوان المشكلة من "مشاكل التبعية الدورية الخطيرة" إلى "التبعيات الدورية" - لقد تحسن الوضع كثيرًا!

kumavis بالتأكيد شيء. ستعمل عليها عندما يسمح الوقت بذلك.

إليكم الوضع الحالي لتنظيف الاعتماد المتبادل كما أراه:
(تظهر الأسهم الوصلات التي يجب إزالتها إذا كان ذلك مناسبًا)

  • [x] المواد
  • [x] الهندسة
  • [x] Object3D
  • [x] الرياضيات
  • [x] الأشكال

    • [x] الشكل -> FontUtils

    • [x] الشكل -> قياس الأبعاد الخارجية

    • [x] الشكل -> الشكل الهندسي

    • [x] المسار -> الشكل

  • [] Box3

    • [] Box3 -> BufferGeometry

    • [] Box3 -> الهندسة

الأشكال:

image

المربع 3:

image

الرياضيات:

هذه العقد مترابطة ولكنها توفر راحة كافية من خلال ذلك.
image

يبدو أن للشكل تبعيات مع ExtrudeGeometry و ShapeGeometry من خلال كود مثل هذا:

// Convenience method to return ExtrudeGeometry

THREE.Shape.prototype.extrude = function ( options ) {

  var extruded = new THREE.ExtrudeGeometry( this, options );
  return extruded;

};

// Convenience method to return ShapeGeometry

THREE.Shape.prototype.makeGeometry = function ( options ) {

  var geometry = new THREE.ShapeGeometry( this, options );
  return geometry;

};

يبدو الآن أن Shape فئة فرعية من Path ، و ExtrudeGeometry و ShapeGeometry كلاهما فئة فرعية من Geometry . لذا ، يمكنك معرفة التبعيات التي ستحتاج إلى التخلص منها في الحالة المثالية.

نعم ، هذا يندرج تحت نفس فئة Vector3 <-> Matrix4 . هم مرتبطون للراحة. أعتقد أنها فكرة سيئة ولكنها لا تستحق محاربة تلك الفكرة. سأضع علامة على أنه مكتمل

يمكن إزالة Shape -> FontUtils باستخدام طرق نقل مثل triangulate إلى Utils أكثر عمومية. لكن ليس فوزًا كبيرًا للقيام بذلك. سنقوم بوضع علامة عليه كمكتمل.

يمكن تنظيف كل من Box3 -> BufferGeometry و Box3 -> Geometry .

إنها حالة أخرى من عدم وضع السلوك المعتمد على الطبقة على الطبقة نفسها.

المصدر :

setFromObject: function () {

  // Computes the world-axis-aligned bounding box of an object (including its children),
  // accounting for both the object's, and childrens', world transforms

  /* ... */

  if ( geometry instanceof THREE.Geometry ) {
    /* ... */
  } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) {
    /* ... */
  }

  /* ... */

}

في كلتا الحالتين ، تحاول فقط التكرار من خلال العالم الرؤوس / المواضع المنسقة للهندسة. أتساءل عما إذا كان من شأنه تبسيط الكود إلى حد كبير لإنشاء كائن vertices كسول على BufferGeometry يبحث عن القيم كما هي مطلوبة. لست متأكدا من تأثير الأداء.

بالتناوب ، يمكننا استخدام Geometry.computeBoundingBox :
Geometry
BufferGeometry

Box3 هو نقطة مشكلة عند تشغيل بنية المستعرض. انظر الملاحظات في coballast / threejs-browserify-conversion-Utility # 21.

kumavis هل تمانع في تحديد الحل الموصى به للتعامل مع Box3 / Geometry / BufferGeometry؟ إذا كان سريعًا ، يمكنني تنفيذه.

لا يمكنني النظر إليه الآن ، لكنني سأبدأ باقتراحي أعلاه لاستخدام geo.computeBoundingBox كما تم تنفيذه على Geometry و BufferGeometry بدلاً من if / else هنا . بدلاً من ذلك ، يجب أن يستدعي $ # box3.setFromObj geometry.computeBoundingBox ثم يقوم بتعيين المعلمات بناءً على المربع الناتج 3.

يجب أن يزيل ذلك Box3 -> BufferGeometry و Box3 -> Geometry نهاية الدعامات الدائرية. اسمحوا لي أن أعرف إذا كنت في عداد المفقودين شيء.

حسنًا ، ربما يكون الرمز الناتج معقدًا بعض الشيء ، ما الذي يبدو منطقيًا هنا؟ لا ينبغي أن يكون Box3.setFromObject موجودًا ، لكن هذا ليس خيارًا. يجب أن يكون Geo قادرًا على إنتاج box3s ، ليس لدي مشكلة في ذلك. نعم أعتقد أن Box3.setFromObject يجب أن يطلب من الموقع الجغرافي المربع المحيط / النطاقات ، ولكن ربما يجب عليهم طلب Object3D / Mesh للمربع المحيط / النطاقات.

آسف حصلت قليلا متوترة. lemme تعرف ما هو رأيك.

ربما يكون ذا صلة: # 6546

بدون شيء مثل هذا ، من المستحيل تحليل تلك التبعيات الديناميكية من البرامج النصية المحمل.

بناءً على الاختبارات التي أجريتها ، لا تعد التبعيات الدورية مشكلة في CommonJSification. يجب التعامل معها بشكل صحيح ، وكما ذكرنا سابقًا في هذا الموضوع ، فإنها تجعل الرسم البياني للتبعية في حالة من الفوضى ، لكنها لا تمنع THREE.js من العمل في بيئات JS الشائعة (عند التحويل ، بالطبع).

لقد قمت للتو بنشر نسخة Commonjs كاملة على npm كـ three.cjs باستخدام مترجمي الثلاثة المشتركين

ملاحظة: لكي يعمل هذا ، كان عليّ يدويًا اختيار cherrypick # 6546 على الماجستير. بينما تعمل التبعيات الديناميكية بشكل جيد في node.js ، لا يمكنها العمل في Browserify (أو أي أداة cjs أخرى إلى المستعرض) لأنها تحتاج إلى إجراء تحليل التبعية الثابت.

دليل التصفح: http://requirebin.com/؟gist=b7fe528d8059a7403960

kamicane FYI - هنا تمت إضافة ثلاثة كوسيطة لدالة مجهولة إلى Raycaster (سابقًا Ray ).

أفهم الحاجة إلى وظيفة مجهولة (لمنع التسربات العالمية في المتصفحات) ، لكن الحجة غير ضرورية ، وتجعل الملف بأكمله يندرج في فئة التبعيات المحسوبة. بينما يمكن إزالة الحجة ديناميكيًا باستخدام تعديلات AST ، لا يمكن أبدًا أن تكون حلاً مضادًا للرصاص (اعتمادًا على ما يتم كتابته في الحجة ، أو قراءتها من الحجة ، وما إلى ذلك). يصبح التحليل الساكن شبه مستحيل. التدخل اليدوي مطلوب في هذه الحالة.

الآن بما أن Raycaster أكثر خفة ، يمكننا أن نمنحه الفرصة لجعله مثل بقية الفصول الدراسية.

mrdoob ، @ Mugen87 نستخدم Rollup لاستخدام أجزاء Three.js التي نحتاجها حقًا فقط. عندما نقوم بتشغيل المبنى ما زلنا نتلقى التحذير التالي:

(!) Circular dependency: node_modules/three/src/math/Vector3.js -> node_modules/three/src/math/Matrix4.js -> node_modules/three/src/math/Vector3.js
(!) Circular dependency: node_modules/three/src/math/Vector3.js -> node_modules/three/src/math/Quaternion.js -> node_modules/three/src/math/Vector3.js
(!) Circular dependency: node_modules/three/src/math/Sphere.js -> node_modules/three/src/math/Box3.js -> node_modules/three/src/math/Sphere.js
(!) Circular dependency: node_modules/three/src/objects/LineSegments.js -> node_modules/three/src/objects/Line.js -> node_modules/three/src/objects/LineSegments.js

هل لا تزال هناك تبعيات دائرية في Three.js أم أننا نفعل شيئًا خاطئًا؟

Vector3 و Matrix4 مرتبطان ببعضهما البعض ، إذا قمت بسحب أحدهما ، فأنت بحاجة إلى سحب الآخر. يجب السماح بالتبعية الدائرية من الناحية الفنية.

bhouston نعم أرى ، شكرًا على التلميح. نعم ، يُسمح بالتبعيّات الدائرية ، ولا يسبب التراكمي أي مشاكل ولكني لست متأكدًا مما إذا كان من الممارسات الجيدة أن يكون لديك تبعيات دائرية. Vector3 يعتمد فقط على Matrix4 بسبب multiplyMatrices و getInverse ، لمزيد من التفاصيل انظر (https://github.com/mrdoob/three.js/ blob / dev / src / math / Vector3.js # L315)

@ roomle-build Idk ، يا رجل ، فقط لأنه يشير صراحة إلى مُنشئ Matrix4؟ ماذا عن

    applyMatrix4: function ( m ) {

        var x = this.x, y = this.y, z = this.z;
        var e = m.elements;

        var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );

        this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
        this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
        this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;

        return this;

},

؟

يمكنك القول أنه يمكنك اجتياز {العناصر: [....]} وستنجح ، لكننا نعلم جميعًا أنها تتوقع Matrix4 هناك

لنبدأ بـ Vector3 .

يعتمد Vector3 على Matrix4 بسبب project و unproject :

    project: function () {

        var matrix = new Matrix4();

        return function project( camera ) {

            matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
            return this.applyMatrix4( matrix );

        };

    }(),

    unproject: function () {

        var matrix = new Matrix4();

        return function unproject( camera ) {

            matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
            return this.applyMatrix4( matrix );

        };

    }(),

Vector3 يعتمد على Quaternion بسبب applyEuler و applyAxisAngle :

    applyEuler: function () {

        var quaternion = new Quaternion();

        return function applyEuler( euler ) {

            if ( ! ( euler && euler.isEuler ) ) {

                console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );

            }

            return this.applyQuaternion( quaternion.setFromEuler( euler ) );

        };

    }(),

    applyAxisAngle: function () {

        var quaternion = new Quaternion();

        return function applyAxisAngle( axis, angle ) {

            return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );

        };

    }(),

اقتراحات؟

لست متأكدًا مما إذا كنا بحاجة إلى إزالة التبعيات الدائرية بكل الوسائل. لكن يمكنني تخيل نقل multiplyMatrices إلى وحدة Math . سيتغير التوقيع بالطبع إلى multiplyMatrices( a: Matrix4, b: Matrix4, result: Matrix4 ): Matrix4 . داخل Vector3 يمكنك بعد ذلك import { multiplyMatrices } from './Math'; يمكن عمل نفس الشيء في Matrix4 (للحفاظ على سطح API Matrix4 كما هو).

لقد ألقيت نظرة سريعة (لم ألقي نظرة على الحالة Quaternian - فقط Vec3/Mat4 ) وأنا أيضًا لست متأكدًا من تداعيات الأداء وعواقبه لبقية قاعدة الكود. علاوة على ذلك ، لست مقتنعًا بضرورة إزالة هذه التبعيات الدائرية. أردت فقط مشاركة أفكاري لأن mrdoob طلب اقتراحات

@ roomle-build لذا قم بإنشاء المزيد من الوحدات من أجل عدم وجود تبعيات دائرية ، لكنك ما زلت ستستخدم كل هذه الوحدات؟ قد يكون هذا أكثر منطقية إذا كانت كل طريقة حسابية ستكون وحدة خاصة بها ، فأنت تسحب فقط تلك التي تستخدمها ، لكن هذا سيكون الكثير من الوحدات.

makc ليس حقًا. ستكون وحدة واحدة كبيرة بها الكثير من وظائف "المساعد" الصغيرة. قد يساعد هذا أيضًا في اهتزاز الأشجار وما إلى ذلك. يمكن أن تبدو وحدة الرياضيات كما يلي:

export const multiplyMatrices( a, b, result ) { // ... DO STUFF ... // }
export const getInverse( /* ... */ ) { // ... DO STUFF ... // }
// ...
// ...

والوحدة المستهلكة ستفعل شيئًا مثل:

import { Matrix4 } from './Matrix4.js';
import { multiplyMatrices } from './math';
const result = new Matrix4( );
multiplyMatrices( a, b, result );

عند تجميع كل شيء معًا ، فإن التجميع يكون أمرًا سحريًا ويخلق الحزمة الأكثر فاعلية.

هذا ما تفعله الكثير من المكتبات الشعبية. في الواقع ، قامت RxJS بتحويل "منطق" import أيضًا إلى النمط الذي وصفته. هناك يبدو مثل:

 import { flatMap, map, tap } from 'rxjs/operators';

myObject.run().pipe(
  tap(result => doSomething()), 
  flatMap(() => doSomethingElse()), 
  map(() => doAnotherThing())
);

يمكنك أن تقرأ عن "لماذا وكيف" قاموا بتغيير هذه الأشياء في RxJS 6 في عدة مدونات على سبيل المثال: https://auth0.com/blog/whats-new-in-rxjs-6/

لكن كما قلت ، إنها مجرد فكرة ولست متأكدًا من جميع الآثار المترتبة على ذلك على بقية قاعدة البيانات. كما أن الوحدة النمطية الحالية math ليست "معدة" لاستخدامها على هذا النحو. حاليًا ، يتم إرفاق جميع الطرق الموجودة في وحدة الرياضيات "نوعًا من السكون". هذا أيضًا يمنع التراكمية من اكتشاف ما هو مطلوب حقًا ...

@ roomle-build hmm ، لذا فأنت تقول أن التجميع يمكنه فهم ما إذا كان الكود الموجود في نفس النطاق لا يحتاج فعليًا إلى النطاق بالكامل ، رائع.

أنت تتحدث عن التحرك نحو نهج وظيفي (تأخذ الوظائف الكائنات) بدلاً من نهج موجه للكائن (كائن له وظائف عضو.) هذا شيء حقيقي ولكن بالنظر إلى أن Three.JS موجه بالكامل نحو الكائن ، فإن اقتراح هذا النوع من التغيير هو كبيرة جدًا وستكسر جميع الشفرات الموجودة.

لست متأكدًا من أن الحجج المؤيدة لهذا التغيير مهمة في هذه المرحلة لتبرير كسر كل التوافق مع الإصدارات السابقة.

makc ليس حقًا. ستكون وحدة واحدة كبيرة بها الكثير من وظائف "المساعد" الصغيرة. قد يساعد هذا أيضًا في اهتزاز الأشجار وما إلى ذلك. يمكن أن تبدو وحدة الرياضيات كما يلي:

إذا كان هذا هو ما تم اقتراحه ، فيجب وصفه بشكل صحيح. إنه تغيير Three.JS من أسلوب التصميم الموجه للكائنات إلى تصميم وظيفي.

@ roomle-build hmm ، لذا فأنت تقول أن التجميع يمكنه فهم ما إذا كان الكود الموجود في نفس النطاق لا يحتاج فعليًا إلى النطاق بالكامل ، رائع.

تتفهم ميزة "نعم التراكمية" كيف ترتبط جميع الواردات ببعضها البعض وتقوم بهز الشجرة والقضاء على الشفرات الميتة وما إلى ذلك. لكن هيكل المشروع الحالي لا يستفيد استفادة كاملة من هذه الميزات.

أنت تتحدث عن التحرك نحو نهج وظيفي (تأخذ الوظائف الكائنات) بدلاً من نهج موجه للكائن (كائن له وظائف عضو.) هذا شيء حقيقي ولكن بالنظر إلى أن Three.JS موجه بالكامل نحو الكائن ، فإن اقتراح هذا النوع من التغيير هو كبيرة جدًا وستكسر جميع الشفرات الموجودة.

لا أعتقد أن هذين النموذجين متنافيان. أعتقد أنه يمكنك خلط ومطابقة هذين النموذجين. كما أنني لا أقترح التغيير إلى البرمجة الوظيفية. أردت فقط وصف طريقة للتخلص من التبعية الدورية. يمكنك أيضًا إرفاق الطريقة multiplyMatrices Math . ولكن إذا أعاد شخص ما كتابة هذا النوع من الأشياء ، فمن المنطقي التفكير في استخدام ميزات وحدات ES6. لكن كما قلت ، لست خبيراً في قاعدة كود Three.js وكان مجرد فكرة عن كيفية التخلص من التبعية الدورية. أعتقد أن Three.js مشروع رائع بقاعدة شفرة رائعة ولا أريد التذمر. لذلك آمل ألا يشعر أحد بالإهانة من تعليقاتي 😉

لست متأكدًا مما إذا كان ينبغي لنا مناقشة قرارات التصميم في إحدى المشكلات. هل لديك مكان حيث يناسب هذا النوع من الأشياء بشكل أفضل؟

BTW gl-matrix هي مكتبة رياضيات وظيفية: https://github.com/toji/gl-matrix/tree/master/src/gl-matrix

@ roomle-build

حاليًا ، يتم إرفاق جميع الطرق الموجودة في وحدة الرياضيات "نوعًا من السكون".

كيف ذلك؟

mrdoob أعتقد أنه مع التصميم الوظيفي لـ gl-matrix ، يتم تصدير كل وظيفة من وظائف say vec3 (في ملف vec3 التي ربطتها في تعليقي السابق) بشكل فردي. يتيح لك ذلك انتقاء واختيار الوظائف المراد استيرادها. لا تحتاج إلى إحضار جميع ملفات vec3.

كما هو الحال مع Three.JS ، نظرًا لأنه يستخدم تصميمًا موجهًا للكائنات ، يتم إرفاق جميع وظائف الرياضيات لـ Vector3 بالنموذج الأولي لكائن Vector3 ويمكنك استيراد فئة Vector3 نفسها فقط.

وبالتالي ، فإن عمليات الاستيراد في Three.JS هي لفئات كاملة بينما باستخدام نهج وظيفي ، يمكنك استيراد وظائف فردية.

(الشيء الآخر الرائع في مكتبة gl-matrix هو أن جميع الوظائف الفردية لا تستخدم وظائف أخرى ، وقد تضمنtoji بشكل أساسي نسخة محسّنة من جميع الرياضيات في كل عملية فردية. ومن المحتمل أن يكون هذا فعالاً للغاية من حيث السرعة لكنه يؤدي إلى صعوبة الحفاظ على المكتبة.)

لا أعتقد أننا بحاجة إلى إعادة تشكيل هذا الجزء من Three.js إلا ربما للتخلص من أي إشارات في / الرياضيات إلى أدلة أخرى في three.js. مكتبة الرياضيات صغيرة نسبيًا ولا تظهر أبدًا في اختبارات التصنيف الخاصة بي هذه الأيام. نعم ، إنها ليست فعالة إلى أقصى حد ، لكنها قريبة بدرجة كافية مع سهولة القراءة وسهولة الاستخدام.

bhouston فهمت ذلك. شكرا جزيلا على الشرح! 😊

أردت فقط متابعة الموضوع. لكني أريد العودة من importing function vs importing classes إلى موضوع حل cyclic dependencies . (لا أرى أيضًا سبب كون import { someFunction } from 'SomeModule' أقل قابلية للصيانة من import SomeClass from 'SomeModule' ، لكن هذا بالتأكيد ليس موضوع هذه المشكلة / المحادثة.

لحل التبعية الدورية ، سيكون من الممكن وضع الوظيفة في فئة منفصلة. يمكنك إرفاق طريقة multiplyMatrices بفئة الرياضيات أو إنشاء فئة مضاعف لها طريقة multiplyMatrices . لكن كما قلت من قبل ، لست متأكدًا مما إذا كان يتعين علينا إزالة التبعيات الدورية. إذا كان القرار هو عدم إزالتها ، أعتقد أن هذه المشكلة قد تكون قريبة

بعد حل # 19137 ، يمكن إغلاق هذا الآن 🎉.

@ Mugen87 نجاح باهر ، 5 سنوات! تهانينا على ضربه أخيرًا: fire:: clap:
لقد استمتعت كثيرًا بعمل تلك الرسوم البيانية في ذلك الوقت: smile_cat:

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات