root/src/kits/interface/Polygon.cpp
/*
 * Copyright 2001-2009, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT license.
 *
 * Authors:
 *              Stephan Aßmus, superstippi@gmx.de
 *              Marc Flerackers, mflerackers@androme.be
 *              Marcus Overhagen
 */


#include <Polygon.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <AffineTransform.h>


// Limit to avoid integer overflow when calculating the size to allocate
#define MAX_POINT_COUNT 10000000


BPolygon::BPolygon(const BPoint* points, int32 count)
        :
        fBounds(0.0f, 0.0f, -1.0f, -1.0f),
        fCount(0),
        fPoints(NULL)
{
        _AddPoints(points, count, true);
}


BPolygon::BPolygon(const BPolygon& other)
        :
        fBounds(0.0f, 0.0f, -1.0f, -1.0f),
        fCount(0),
        fPoints(NULL)
{
        *this = other;
}


BPolygon::BPolygon(const BPolygon* other)
        :
        fBounds(0.0f, 0.0f, -1.0f, -1.0f),
        fCount(0),
        fPoints(NULL)
{
        *this = *other;
}


BPolygon::BPolygon()
        :
        fBounds(0.0f, 0.0f, -1.0f, -1.0f),
        fCount(0),
        fPoints(NULL)
{
}


BPolygon::~BPolygon()
{
        free(fPoints);
}


BPolygon&
BPolygon::operator=(const BPolygon& other)
{
        // Make sure we aren't trying to perform a "self assignment".
        if (this == &other)
                return *this;

        free(fPoints);
        fPoints = NULL;
        fCount = 0;
        fBounds.Set(0.0f, 0.0f, -1.0f, -1.0f);

        if (_AddPoints(other.fPoints, other.fCount, false))
                fBounds = other.fBounds;

        return *this;
}


BRect
BPolygon::Frame() const
{
        return fBounds;
}


void
BPolygon::AddPoints(const BPoint* points, int32 count)
{
        _AddPoints(points, count, true);
}


int32
BPolygon::CountPoints() const
{
        return fCount;
}


void
BPolygon::MapTo(BRect source, BRect destination)
{
        for (uint32 i = 0; i < fCount; i++)
                _MapPoint(fPoints + i, source, destination);

        _MapRectangle(&fBounds, source, destination);
}


void
BPolygon::PrintToStream() const
{
        for (uint32 i = 0; i < fCount; i++)
                fPoints[i].PrintToStream();
}


//void
//BPolygon::TransformBy(const BAffineTransform& transform)
//{
//      transform.Apply(fPoints, (int32)fCount);
//      fBounds = _ComputeBounds(fPoints, fCount);
//}
//
//
//BPolygon&
//BPolygon::TransformBySelf(const BAffineTransform& transform)
//{
//      TransformBy(transform);
//      return *this;
//}
//
//
//BPolygon
//BPolygon::TransformByCopy(const BAffineTransform& transform) const
//{
//      BPolygon copy(this);
//      copy.TransformBy(transform);
//      return copy;
//}


// #pragma mark - BPolygon private methods


bool
BPolygon::_AddPoints(const BPoint* points, int32 count, bool computeBounds)
{
        if (points == NULL || count <= 0)
                return false;
        if (count > MAX_POINT_COUNT || (fCount + count) > MAX_POINT_COUNT) {
                fprintf(stderr, "BPolygon::_AddPoints(%" B_PRId32 ") - too many points"
                        "\n", count);
                return false;
        }

        BPoint* newPoints = (BPoint*)realloc((void*)fPoints, (fCount + count)
                * sizeof(BPoint));
        if (newPoints == NULL) {
                fprintf(stderr, "BPolygon::_AddPoints(%" B_PRId32 ") out of memory\n",
                        count);
                return false;
        }

        fPoints = newPoints;
        memcpy((void*)(fPoints + fCount), points, count * sizeof(BPoint));
        fCount += count;

        if (computeBounds)
                fBounds = _ComputeBounds(fPoints, fCount);

        return true;
}


BRect
BPolygon::_ComputeBounds(const BPoint* points, uint32 count)
{
        if (count == 0)
                return BRect(0.0, 0.0, -1.0f, -1.0f);

        BRect bounds(points[0], points[0]);

        for (uint32 i = 1; i < count; i++) {
                if (points[i].x < bounds.left)
                        bounds.left = points[i].x;

                if (points[i].y < bounds.top)
                        bounds.top = points[i].y;

                if (points[i].x > bounds.right)
                        bounds.right = points[i].x;

                if (points[i].y > bounds.bottom)
                        bounds.bottom = points[i].y;
        }

        return bounds;
}


void
BPolygon::_MapPoint(BPoint* point, const BRect& source,
        const BRect& destination)
{
        point->x = (point->x - source.left) * destination.Width() / source.Width()
                + destination.left;
        point->y = (point->y - source.top) * destination.Height() / source.Height()
                + destination.top;
}


void
BPolygon::_MapRectangle(BRect* rect, const BRect& source,
        const BRect& destination)
{
        BPoint leftTop = rect->LeftTop();
        BPoint bottomRight = rect->RightBottom();

        _MapPoint(&leftTop, source, destination);
        _MapPoint(&bottomRight, source, destination);

        *rect = BRect(leftTop, bottomRight);
}