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;
}
}
और फिर ... ओह प्रतीक्षा करें, हम काम हो गया!
आप व्यक्तिगत क्यूब्स/क्षेत्रों को कैसे बनाते हैं? क्या प्रत्येक घन में अपना माउस इवेंट हो सकता है? – Sphinxxx
@Sphinxxx इस समय cubes 'Geometry3D' से बनाए गए' ModelVisual3D 'हैं। तो मुझे लगता है कि अगर मैं 'UIElement3D' कक्षा का उपयोग करता हूं तो हाँ, उनके पास अपनी घटनाएं हो सकती हैं। लेकिन मुझे नहीं लगता कि व्यक्तिगत इनपुट घटनाएं कैसे मदद कर सकती हैं क्योंकि मुझे यह सुनिश्चित करने के लिए कि एक विस्तृत क्षेत्र (कम से कम डेढ़ cubes) की जांच करने की आवश्यकता है ताकि उपयोगकर्ता उस घूर्णन को चाहे। – Paul
मैं इस सवाल में उल्लेख करना भूल गया कि घूर्णन को वही काम करना चाहिए, उदाहरण के लिए, अब मेरे पास एक नीली पंक्ति और शीर्ष पर एक सफेद पंक्ति है। – Paul