2013-01-08 11 views
5

में स्थान के अनुसार मैं WPF में एक Viewport3D करने के लिए कई क्यूब्स जोड़ा गया है और अब मैं माउस के साथ उनमें से समूहों में हेरफेर करना चाहते हैं।घुमाएँ मॉडल समूह माउस खींचें दिशा और मॉडल

initial cube

जब मैं एक से अधिक & खींचें और उन क्यूब्स मैं छेद विमान दिशा कि खींचें बनाया गया था में घुमाया चाहते का आधा क्लिक करें, रोटेशन RotateTransform3D द्वारा नियंत्रित किया जाएगा तो यह नहीं होगा एक समस्या।

समस्या यह है कि मुझे नहीं पता कि मुझे ड्रैग को कैसे संभालना चाहिए, अधिक सटीक: मुझे यह कैसे पता चलेगा कि किन विमानों को घूमने के लिए क्यूब्स के चेहरे खींचे गए थे?

उदाहरण के लिए नीचे दिए गए मामले में मैं जानना चाहता हूं कि मुझे 9 0 डिग्री दक्षिणावर्त के साथ क्यूब्स के दाहिने विमान को घुमाए जाने की आवश्यकता है ताकि नीले रंग के चेहरों की पंक्ति सफेद रंगों की बजाय शीर्ष पर होगी पीछे।

second cube

और इस उदाहरण में ऊपर परत 90 डिग्री वामावर्त घुमाया जाना चाहिए: third cube

वर्तमान में मेरा विचार जाँच करने के लिए घन से अधिक अदृश्य क्षेत्रों के कुछ प्रकार जगह है, जो एक में

enter image description here

0123: खींचें VisualTreeHelper.HitTest साथ क्या हो रहा है और उसके बाद निर्धारित करने के लिए जो विमान मैं बारी बारी से करना चाहिए, इस क्षेत्र पहले खींचें उदाहरण से मेल खाएगी

लेकिन जब मैं सभी चार क्षेत्रों को जोड़ता हूं तो मैं वापस वर्ग में वापस आ जाता हूं क्योंकि मुझे अभी भी दिशा को निर्धारित करने की आवश्यकता है और किन क्षेत्रों को घुमाया जाना चाहिए, जिसके अनुसार "स्पर्श" किए गए थे।

मैं विचारों के लिए खुला हूं।

कृपया ध्यान दें कि इस घन स्वतंत्र रूप से ले जाया जा सकता है, इसलिए यह प्रारंभिक स्थिति में नहीं हो सकता है जब उपयोगकर्ता क्लिक करता है और खींच लेता है, यह क्या मुझे परेशान सबसे अधिक है।

पीएस: ड्रैग MouseLeftButtonDown, MouseMove और MouseLeftButtonUp के संयोजन के साथ कार्यान्वित किया जाएगा।

+0

आप व्यक्तिगत क्यूब्स/क्षेत्रों को कैसे बनाते हैं? क्या प्रत्येक घन में अपना माउस इवेंट हो सकता है? – Sphinxxx

+0

@Sphinxxx इस समय cubes 'Geometry3D' से बनाए गए' ModelVisual3D 'हैं। तो मुझे लगता है कि अगर मैं 'UIElement3D' कक्षा का उपयोग करता हूं तो हाँ, उनके पास अपनी घटनाएं हो सकती हैं। लेकिन मुझे नहीं लगता कि व्यक्तिगत इनपुट घटनाएं कैसे मदद कर सकती हैं क्योंकि मुझे यह सुनिश्चित करने के लिए कि एक विस्तृत क्षेत्र (कम से कम डेढ़ cubes) की जांच करने की आवश्यकता है ताकि उपयोगकर्ता उस घूर्णन को चाहे। – Paul

+0

मैं इस सवाल में उल्लेख करना भूल गया कि घूर्णन को वही काम करना चाहिए, उदाहरण के लिए, अब मेरे पास एक नीली पंक्ति और शीर्ष पर एक सफेद पंक्ति है। – Paul

उत्तर

3

MouseEvents

आप VisualTreeHelper.HitTest() उपयोग करने के लिए Visual3D वस्तुओं (प्रक्रिया आसान हो सकता है, तो प्रत्येक चेहरा एक अलग ModelVisual3D है) लेने की आवश्यकता होगी। Here सामान्य रूप से हिटटेस्टिंग पर कुछ मदद है, और here एक बहुत उपयोगी बोली है जो पिकिंग प्रक्रिया को काफी सरल बनाता है।

घटना मुर्गियों को मारने

मान लीजिए कि आप अब अपनी पिकिंग परीक्षण (MouseDown घटना से एक, MouseUp घटना से एक) से दो ModelVisual3D वस्तुओं है कि करते हैं। सबसे पहले, हमें पता होना चाहिए कि क्या वे coplanar हैं (एक चेहरे से दूसरे चेहरे पर जाने वाली चुनौतियों से बचने के लिए)। ऐसा करने का एक तरीका यह है कि वे सामान्य दिशाओं की तुलना करें, यह देखने के लिए कि क्या वे एक ही दिशा को इंगित कर रहे हैं। यदि आपने अपने मेश जीमेट्री 3 डी में सामान्य परिभाषित किए हैं, तो यह बहुत अच्छा है। यदि नहीं, तो हम अभी भी इसे पा सकते हैं। मैं विस्तार के लिए एक स्थिर वर्ग जोड़ने का सुझाव देना चाहूंगा।एक सामान्य की गणना का एक उदाहरण:

public static class GeometricExtensions3D 
{ 
    public static Vector3D FaceNormal(this MeshGeometry3D geo) 
    { 
     // get first triangle's positions 
     var ptA = geo.Positions[geo.TriangleIndices[0]]; 
     var ptB = geo.Positions[geo.TriangleIndices[1]]; 
     var ptC = geo.Positions[geo.TriangleIndices[2]]; 
     // get specific vectors for right-hand normalization 
     var vecAB = ptB - ptA; 
     var vecBC = ptC - ptB; 
     // normal is cross product 
     var normal = Vector3D.CrossProduct(vecAB, vecBC); 
     // unit vector for cleanliness 
     normal.Normalize(); 
     return normal; 
    } 
} 

इस उपयोग करके आप अपने Visual3D हिट (यहां शामिल कास्टिंग के बहुत सारे) से MeshGeometry3D की normals तुलना करें और देखें कि वे एक ही दिशा में इशारा करते हैं कर सकते हैं। मैं केवल सुरक्षा के लिए, सीधे समानता के विपरीत वैक्टरों के एक्स, वाई, जेड पर सहिष्णुता परीक्षण का उपयोग करूंगा। एक और विस्तार उपयोगी हो सकता है:

public static double SSDifference(this Vector3D vectorA, Vector3D vectorB) 
    { 
     // set vectors to length = 1 
     vectorA.Normalize(); 
     vectorB.Normalize(); 
     // subtract to get difference vector 
     var diff = Vector3D.Subtract(vectorA, vectorB); 
     // sum of the squares of the difference (also happens to be difference vector squared) 
     return diff.LengthSquared; 
    } 

अगर वे समतलीय नहीं कर रहे हैं (SSDifference> कुछ मनमाने ढंग से परीक्षण मूल्य), आप (या प्रतिक्रिया के कुछ प्रकार दे) यहाँ return कर सकते हैं।

वस्तु चयन

अब है कि हम अपने दो चेहरे निर्धारित किया है और वे कर रहे हैं कि, वास्तव में, हमारे वांछित घटना से निपटने के लिए परिपक्व है, हम हमारे पास क्या है से जानकारी बाहर टकरा के लिए एक रास्ता निकालना चाहिए। आपके पास अभी भी सामान्य मानदंडों की आवश्यकता होनी चाहिए। हम घूमने के लिए बाकी चेहरों को चुनने के लिए फिर से उनका उपयोग करने जा रहे हैं। तुलना का निर्धारण करने के लिए एक चेहरा रोटेशन में शामिल किया जाना चाहिए अगर एक और विस्तार विधि सहायक हो सकता है:

public static bool SharedColumn(this MeshGeometry3D basis, MeshGeometry3D compareTo, Vector3D normal) 
    { 
     foreach (Point3D basePt in basis.Positions) 
     { 
      foreach (Point3D compPt in compareTo.Positions) 
      { 
       var compToBasis = basePt - compPt; // vector from compare point to basis point 
       if (normal.SSDifference(compToBasis) < float.Epsilon) // at least one will be same direction as 
       {              // as normal if they are shared in a column 
        return true; 
       } 
      } 
     } 
     return false; 
    } 

आप अपने meshes (MouseDown और MouseUp) दोनों के लिए चेहरे चुनना करने की आवश्यकता होगी, की सब कुछ खत्म हो पुनरावृत्ति चेहरे। ज्यामिति की सूची को सहेजें जिन्हें घुमाने की आवश्यकता है।

RotateTransform

अब मुश्किल हिस्सा। एक एक्सिस-कोण रोटेशन दो पैरामीटर लेता है: Vector3D रोटेशन (दाएं हाथ के नियम का उपयोग करके) और रोटेशन के कोण के लिए सामान्य धुरी का प्रतिनिधित्व करता है। लेकिन हमारे घन का मध्यबिंदु (0, 0, 0) पर नहीं हो सकता है, इसलिए घूर्णन मुश्किल हो सकता है। Ergo, पहले हमें घन के मध्य बिंदु मिलना चाहिए! सबसे सरल तरीका मैं सोच सकता हूं कि घन में प्रत्येक बिंदु के एक्स, वाई, और जेड घटकों को जोड़ना है और फिर उन्हें अंक की संख्या से विभाजित करना है। चाल, ज़ाहिर है, एक ही बिंदु को एक से अधिक बार नहीं जोड़ना होगा! आप कैसे करते हैं यह इस बात पर निर्भर करेगा कि आपका डेटा कैसे व्यवस्थित होता है, लेकिन मैं इसे एक (अपेक्षाकृत) मामूली अभ्यास मानता हूं। ट्रांसफॉर्म लागू करने के बजाय, आप खुद को अंक ले जाना चाहते हैं, इसलिए ट्रांसफॉर्म ग्रुप बनाने और जोड़ने के बजाय, हम मैट्रिक्स बनाने जा रहे हैं! एक का अनुवाद मैट्रिक्स लगता है:

1, 0, 0, dx 
    0, 1, 0, dy 
    0, 0, 1, dz 
    0, 0, 0, 1 

तो, अपने घन के मध्य को देखते हुए आपके अनुवाद मैट्रिक्स होगा:

var cp = GetCubeCenterPoint(); // user-defined method of retrieving cube's center point 
    // gpu's process matrices in column major order, and they are defined thusly 
    var matToCenter = new Matrix3D(
      1, 0, 0, 0, 
      0, 1, 0, 0, 
      0, 0, 0, 1, 
      -cp.X, -cp.Y, -cp.Z, 1); 
    var matBackToPosition = new Matrix3D(
      1, 0, 0, 0, 
      0, 1, 0, 0, 
      0, 0, 0, 1, 
      cp.X, cp.Y, cp.Z, 1); 

कौन सा बस हमारी रोटेशन छोड़ देता है। क्या आपके पास अभी भी माउस एवेन्ट्स से चुने गए दो मेषों का संदर्भ है? अच्छा! चलो एक और विस्तार को परिभाषित करते हैं:

public static Point3D CenterPoint(this MeshGeometry3D geo) 
    { 
     var midPt = new Point3D(0, 0, 0); 
     var n = geo.Positions.Count; 
     foreach (Point3D pt in geo.Positions) 
     { 
      midPt.Offset(pt.X, pt.Y, pt.Z); 
     } 
     midPt.X /= n; midPt.Y /= n; midPt.Z /= n; 
     return midPt; 
    } 

MouseDown से वेक्टर रों जाल (क्रम महत्वपूर्ण है) के MouseUp लिए जाल 'प्राप्त करें।

var swipeVector = MouseUpMesh.CenterPoint() - MouseDownMesh.CenterPoint(); 

और आपके पास अभी भी हमारे हिट चेहरों के लिए सामान्य है, है ना?

var rotationAxis = Vector3D.CrossProduct(swipeVector, faceNormal); 

जो आपके रोटेशन कोण हमेशा 90 ° कर देगा: हम (मूल रूप से जादुई) द्वारा रोटेशन धुरी मिल सकती है।RotationMatrix (source) बनाओ:

swipeVector.Normalize(); 
    var cosT = Math.Cos(Math.PI/2); 
    var sinT = Math.Cos(Math.PI/2); 
    var x = swipeVector.X; 
    var y = swipeVector.Y; 
    var z = swipeVector.Z; 
    // build matrix, remember Column-Major 
    var matRotate = new Matrix3D(
     cosT + x*x*(1 -cosT), y*x*(1 -cosT) + z*sinT, z*x*(1 -cosT) -y*sinT, 0, 
     x*y*(1 -cosT) -z*sinT, cosT + y*y*(1 -cosT), y*z*(1 -cosT) -x*sinT, 0, 
     x*z*(1 -cosT) -y*sinT, y*z*(1 -cosT) -x*sinT, cosT + z*z*(1 -cosT), 0, 
          0,      0,      0, 1); 

परिवर्तन मैट्रिक्स पाने के लिए उन्हें कम्बाइन, ध्यान दें कि महत्वपूर्ण है। हम बिंदु लेना चाहते हैं, इसे मूल के सापेक्ष निर्देशांक में बदलना चाहते हैं, इसे घुमाएं, फिर उसे उस क्रम में मूल निर्देशांक में बदल दें। तो:

var matTrans = Matrix3D.Multiply(Matrix3D.Multiply(matToCenter, matRotate), matBackToPosition); 

फिर, आप अंक को स्थानांतरित करने के लिए तैयार हैं। प्रत्येक MeshGeometry3D जिसे आपने पहले रोटेशन के लिए टैग में प्रत्येक Point3D के माध्यम से पुनरावृति, और कार्य करें:

foreach (MeshGeometry3D geo in taggedGeometries) 
    { 
     for (int i = 0; i < geo.Positions.Count; i++) 
     { 
      geo.Positions[i] *= matTrans; 
     } 
    } 

और फिर ... ओह प्रतीक्षा करें, हम काम हो गया!

+0

धन्यवाद! मैंने आपका जवाब बहुत तेज़ पढ़ा, लेकिन मैं कल इसमें गोता लगाने जा रहा हूं क्योंकि अभी मैं बहुत थक गया हूं। +1 – Paul

संबंधित मुद्दे