سؤال

لدي إحداثيات النقطة العلوية اليسرى للمستطيل بالإضافة إلى عرضه وارتفاعه ودورانه من 0 إلى 180 ومن -0 إلى -180.

أحاول الحصول على الإحداثيات المحيطة بالمربع الفعلي حول المستطيل.

ما هي طريقة بسيطة لحساب إحداثيات المربع المحيط

  • مين ص، ماكس ص، دقيقة س، ماكس س؟

النقطة A ليست دائما على الحد الأدنى، يمكن أن تكون في أي مكان.

يمكنني استخدام مصفوفة مجموعة أدوات التحويل في as3 إذا لزم الأمر.

هل كانت مفيدة؟

المحلول

  • تحويل إحداثيات جميع الزوايا الأربع
  • ابحث عن الأصغر بين علامات x الأربعة min_x
  • ابحث عن أكبر علامة x الأربعة وقم بتسميتها max_x
  • كما سبق مع ذ
  • المربع المحيط الخاص بك هو (min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)

أفيك، لا يوجد أي طريق ملكي سيوصلك إلى هناك بشكل أسرع.

إذا كنت تتساءل عن كيفية تحويل الإحداثيات، فجرب:

x2 = x0+(x-x0)*cos(theta)+(y-y0)*sin(theta)
y2 = y0-(x-x0)*sin(theta)+(y-y0)*cos(theta)

حيث (x0,y0) هو المركز الذي تدور حوله.قد تحتاج إلى العبث بهذا اعتمادًا على وظائف علم المثلثات الخاصة بك (هل يتوقعون درجات أو راديان) إحساس/علامة نظام الإحداثيات الخاص بك مقابل.كيف تحدد الزوايا، وما إلى ذلك.

نصائح أخرى

أدرك أنك تطلب ActionScript، ولكن في حالة وصول أي شخص إلى هنا للبحث عن إجابة iOS أو OS-X، فهو كالتالي:

+ (CGRect) boundingRectAfterRotatingRect: (CGRect) rect toAngle: (float) radians
{
    CGAffineTransform xfrm = CGAffineTransformMakeRotation(radians);
    CGRect result = CGRectApplyAffineTransform (rect, xfrm);

    return result;
}

إذا كان نظام التشغيل الخاص بك يعرض عليك القيام بكل العمل الشاق نيابةً عنك، فاسمح له بذلك!:)

سويفت:

func boundingRectAfterRotatingRect(rect: CGRect, toAngle radians: CGFloat) -> CGRect {
    let xfrm = CGAffineTransformMakeRotation(radians)
    return CGRectApplyAffineTransform (rect, xfrm)
}

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

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

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

ct = cos( theta );
st = sin( theta );

hct = h * ct;
wct = w * ct;
hst = h * st;
wst = w * st;

if ( theta > 0 )
{
    if ( theta < 90 )
    {
        // 0 < theta < 90
        y_min = A_y;
        y_max = A_y + hct + wst;
        x_min = A_x - hst;
        x_max = A_x + wct;
    }
    else
    {
        // 90 <= theta <= 180
        y_min = A_y + hct;
        y_max = A_y + wst;
        x_min = A_x - hst + wct;
        x_max = A_x;
    }
}
else
{
    if ( theta > -90 )
    {
        // -90 < theta <= 0
        y_min = A_y + wst;
        y_max = A_y + hct;
        x_min = A_x;
        x_max = A_x + wct - hst;
    }
    else
    {
        // -180 <= theta <= -90
        y_min = A_y + wst + hct;
        y_max = A_y;
        x_min = A_x + wct;
        x_max = A_x - hst;
    }
}

يفترض هذا النهج أن لديك ما تقول أنك تمتلكه، على سبيل المثال.النقطة A وقيمة ثيتا تقع في النطاق [-180، 180].لقد افترضت أيضًا أن ثيتا تزداد في اتجاه عقارب الساعة لأن هذا هو ما يشير إليه المستطيل الذي تم تدويره بمقدار 30 درجة في الرسم التخطيطي الذي تستخدمه، ولم أكن متأكدًا مما كان يحاول الجزء الموجود على اليمين الإشارة إليه.إذا كانت هذه هي الطريقة الخاطئة، فما عليك سوى تبديل الجمل المتماثلة وكذلك إشارة الحدود st.

    fitRect: function( rw,rh,radians ){
            var x1 = -rw/2,
                x2 = rw/2,
                x3 = rw/2,
                x4 = -rw/2,
                y1 = rh/2,
                y2 = rh/2,
                y3 = -rh/2,
                y4 = -rh/2;

            var x11 = x1 * Math.cos(radians) + y1 * Math.sin(radians),
                y11 = -x1 * Math.sin(radians) + y1 * Math.cos(radians),
                x21 = x2 * Math.cos(radians) + y2 * Math.sin(radians),
                y21 = -x2 * Math.sin(radians) + y2 * Math.cos(radians), 
                x31 = x3 * Math.cos(radians) + y3 * Math.sin(radians),
                y31 = -x3 * Math.sin(radians) + y3 * Math.cos(radians),
                x41 = x4 * Math.cos(radians) + y4 * Math.sin(radians),
                y41 = -x4 * Math.sin(radians) + y4 * Math.cos(radians);

            var x_min = Math.min(x11,x21,x31,x41),
                x_max = Math.max(x11,x21,x31,x41);

            var y_min = Math.min(y11,y21,y31,y41);
                y_max = Math.max(y11,y21,y31,y41);

            return [x_max-x_min,y_max-y_min];
        }

إذا كنت تستخدم GDI+، فيمكنك إنشاء GrpaphicsPath جديد -> إضافة أي نقاط أو أشكال إليه -> تطبيق تحويل التدوير -> استخدم GraphicsPath.GetBounds() وسيُرجع مستطيلًا يحد الشكل المُدار.

(تحرير) نموذج VB.Net

Public Shared Sub RotateImage(ByRef img As Bitmap, degrees As Integer)
' http://stackoverflow.com/questions/622140/calculate-bounding-box-coordinates-from-a-rotated-rectangle-picture-inside#680877
'
Using gp As New GraphicsPath
  gp.AddRectangle(New Rectangle(0, 0, img.Width, img.Height))

  Dim translateMatrix As New Matrix
  translateMatrix.RotateAt(degrees, New PointF(img.Width \ 2, img.Height \ 2))
  gp.Transform(translateMatrix)

  Dim gpb = gp.GetBounds

  Dim newwidth = CInt(gpb.Width)
  Dim newheight = CInt(gpb.Height)

  ' http://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations
  '
  Dim rotatedBmp As New Bitmap(newwidth, newheight)

  rotatedBmp.SetResolution(img.HorizontalResolution, img.VerticalResolution)

  Using g As Graphics = Graphics.FromImage(rotatedBmp)
    g.Clear(Color.White)
    translateMatrix = New Matrix
    translateMatrix.Translate(newwidth \ 2, newheight \ 2)
    translateMatrix.Rotate(degrees)
    translateMatrix.Translate(-img.Width \ 2, -img.Height \ 2)
    g.Transform = translateMatrix
    g.DrawImage(img, New PointF(0, 0))
  End Using
  img.Dispose()
  img = rotatedBmp
End Using

نهاية الفرعية

على الرغم من أن Code Guru ذكر طريقة GetBounds()، فقد لاحظت أن السؤال يحمل علامة as3، flex، لذا إليك مقتطف as3 يوضح الفكرة.

var box:Shape = new Shape();
box.graphics.beginFill(0,.5);
box.graphics.drawRect(0,0,100,50);
box.graphics.endFill();
box.rotation = 20;
box.x = box.y = 100;
addChild(box);

var bounds:Rectangle = box.getBounds(this);

var boundingBox:Shape = new Shape();
boundingBox.graphics.lineStyle(1);
boundingBox.graphics.drawRect(bounds.x,bounds.y,bounds.width,bounds.height);
addChild(boundingBox);

لقد لاحظت أن هناك طريقتين يبدو أنهما تفعلان نفس الشيء:getBounds() و getRect()

/**
     * Applies the given transformation matrix to the rectangle and returns
     * a new bounding box to the transformed rectangle.
     */
    public static function getBoundsAfterTransformation(bounds:Rectangle, m:Matrix):Rectangle {
        if (m == null) return bounds;

        var topLeft:Point = m.transformPoint(bounds.topLeft);
        var topRight:Point = m.transformPoint(new Point(bounds.right, bounds.top));
        var bottomRight:Point = m.transformPoint(bounds.bottomRight);
        var bottomLeft:Point = m.transformPoint(new Point(bounds.left, bounds.bottom));

        var left:Number = Math.min(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x);
        var top:Number = Math.min(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y);
        var right:Number = Math.max(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x);
        var bottom:Number = Math.max(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y);
        return new Rectangle(left, top, right - left, bottom - top);
    }

قم بتطبيق مصفوفة التدوير على نقاط الزاوية الخاصة بك.ثم استخدم الحد الأدنى/الحد الأقصى على التوالي لإحداثيات x وy التي تم الحصول عليها لتحديد المربع المحيط الجديد الخاص بك.

فيما يلي ثلاث وظائف من مكتباتي مفتوحة المصدر.تم اختبار الوظائف بالكامل في Java ولكن يمكن ترجمة الصيغ بسهولة إلى أي لغة.

التوقيعات هي :

getAngleFromPoint العائمة الثابتة العامة (النقطة النهائية centerPoint، النقطة النهائية touchPoint)

تعويم ثابت عام getTwoFingerDistance (تعويم firstTouchX، تعويم firstTouchY، تعويم SecondTouchX، تعويم SecondTouchY)

نقطة getPointFromAngle (الزاوية المزدوجة النهائية، نصف القطر المزدوج النهائي)

يفترض هذا الحل أن كثافة البكسل متباعدة بشكل متساوٍ.قبل تدوير الكائن قم بما يلي:

  1. استخدم getAngleFromPoint لحساب الزاوية من المركز إلى الزاوية اليمنى العليا (لنفترض أن هذا يُرجع 20 درجة) مما يعني أن الزاوية اليسرى العلوية هي -20 درجة أو 340 درجة.

  2. استخدم getTwoFingerDistance لإرجاع المسافة القطرية بين النقطة المركزية والزاوية اليمنى العليا (يجب أن تكون هذه المسافة هي نفسها بشكل واضح لجميع الزوايا، وسيتم استخدام هذه المسافة في الحساب التالي).

  3. لنفترض الآن أننا قمنا بتدوير الجسم بمقدار 30 درجة في اتجاه عقارب الساعة.نعلم الآن أن الزاوية اليمنى العليا يجب أن تكون عند 50 درجة والزاوية العلوية اليسرى عند 10 درجات.

  4. يجب أن تكون الآن قادرًا على استخدام وظيفة getPointFromAngle في الزاوية العلوية اليسرى واليمنى العلوية.باستخدام نصف القطر الذي تم إرجاعه من الخطوة 2.يجب أن يمنحك موضع X مضروبًا في 2 من الزاوية اليمنى العليا العرض الجديد ويجب أن يمنحك موضع Y مضروبًا في 2 من الزاوية اليسرى العليا الارتفاع الجديد.

يجب وضع هذه الخطوات الأربع المذكورة أعلاه في شروط بناءً على مدى تدوير الكائن الخاص بك، وإلا يمكنك إرجاع الارتفاع كعرض والعرض كارتفاع.

ضع في اعتبارك أن وظائف الزاوية يتم التعبير عنها بعوامل من 0-1 بدلاً من 0-360 (فقط قم بالضرب أو القسمة على 360 حيثما كان ذلك مناسبًا):

// الحصول على زاوية من نقطتين معبرًا عنها كعامل 0 -1 (0 يمثل 0/360، 0.25 يمثل 90 درجة وما إلى ذلك)

public float getAngleFromPoint(final Point centerPoint, final Point touchPoint) {

    float returnVal = 0;

    //+0 - 0.5
    if(touchPoint.x > centerPoint.x) {

        returnVal = (float) (Math.atan2((touchPoint.x - centerPoint.x), (centerPoint.y - touchPoint.y)) * 0.5 / Math.PI);

    }
    //+0.5
    else if(touchPoint.x < centerPoint.x) {

        returnVal = (float) (1 - (Math.atan2((centerPoint.x - touchPoint.x), (centerPoint.y - touchPoint.y)) * 0.5 / Math.PI));

    }//End if(touchPoint.x > centerPoint.x)

    return returnVal;

}

// يقيس المسافة القطرية بين نقطتين

public float getTwoFingerDistance(final float firstTouchX, final float firstTouchY, final float secondTouchX, final float secondTouchY) {

    float pinchDistanceX = 0;
    float pinchDistanceY = 0;

    if(firstTouchX > secondTouchX) {

        pinchDistanceX = Math.abs(secondTouchX - firstTouchX);

    }
    else if(firstTouchX < secondTouchX) {

        pinchDistanceX = Math.abs(firstTouchX - secondTouchX);

    }//End if(firstTouchX > secondTouchX)

    if(firstTouchY > secondTouchY) {

        pinchDistanceY = Math.abs(secondTouchY - firstTouchY);

    }
    else if(firstTouchY < secondTouchY) {

        pinchDistanceY = Math.abs(firstTouchY - secondTouchY);

    }//End if(firstTouchY > secondTouchY)

    if(pinchDistanceX == 0 && pinchDistanceY == 0) {

        return 0;

    }
    else {

        pinchDistanceX = (pinchDistanceX * pinchDistanceX);
        pinchDistanceY = (pinchDistanceY * pinchDistanceY);
        return (float) Math.abs(Math.sqrt(pinchDistanceX + pinchDistanceY));

    }//End if(pinchDistanceX == 0 && pinchDistanceY == 0)

}

// احصل على إحداثيات XY من زاوية محددة لنصف القطر (يتم التعبير عن الزاوية بعامل 0-10 يمثل 0/360 درجة و0.75 يمثل 270 وما إلى ذلك)

public Point getPointFromAngle(final double angle, final double radius) {

    final Point coords = new Point();
    coords.x = (int) (radius * Math.sin((angle) * 2 * Math.PI));
    coords.y = (int) -(radius * Math.cos((angle) * 2 * Math.PI));

    return coords;

}

مقتطفات التعليمات البرمجية هذه مأخوذة من مكتباتي مفتوحة المصدر: https://bitbucket.org/warwick/hgdialrepo و https://bitbucket.org/warwick/hacergestov2.أحدهما عبارة عن مكتبة إيماءات لنظام Android والآخر عبارة عن عنصر تحكم في الاتصال لنظام Android.يوجد أيضًا تطبيق OpenGLES 2.0 للتحكم في الاتصال على: https://bitbucket.org/warwick/hggldial

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

في حال لم تكن على دراية بالتعريف الدقيق للمصفوفات، قم بإلقاء نظرة هنا.

لقد استخدمت المنطقة لتدوير المستطيل أولاً ثم استخدم تلك المنطقة التي تم تدويرها لاكتشاف هذا المستطيل

        r = new Rectangle(new Point(100, 200), new Size(200, 200));         
        Color BorderColor = Color.WhiteSmoke;
        Color FillColor = Color.FromArgb(66, 85, 67);
        int angle = 13;
        Point pt = new Point(r.X, r.Y);
        PointF rectPt = new PointF(r.Left + (r.Width / 2),
                               r.Top + (r.Height / 2));
       //declare myRegion globally 
        myRegion = new Region(r);

        // Create a transform matrix and set it to have a 13 degree

        // rotation.
        Matrix transformMatrix = new Matrix();
        transformMatrix.RotateAt(angle, pt);

        // Apply the transform to the region.
        myRegion.Transform(transformMatrix);
        g.FillRegion(Brushes.Green, myRegion);
        g.ResetTransform();

الآن للكشف عن هذا المستطيل

        private void panel_MouseMove(object sender, MouseEventArgs e)
    {


        Point point = e.Location;
        if (myRegion.IsVisible(point, _graphics))
        {
            // The point is in the region. Use an opaque brush.
            this.Cursor = Cursors.Hand;
        }
        else {
            this.Cursor = Cursors.Cross;
        }

    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top