/*
* Copyright (c) 2005-2008 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Polygon entity.
*/
#include
#include
#include
#include
#include
#ifdef HAVE_OPENGL
#include
#endif
#include "vg.h"
#include "vg_view.h"
#include "icons.h"
static void
Init(void *p)
{
VG_Polygon *vp = p;
vp->outline = 0;
vp->pts = NULL;
vp->nPts = 0;
vp->ints = NULL;
vp->nInts = 0;
}
static int
Load(void *p, AG_DataSource *ds, const AG_Version *ver)
{
VG_Polygon *vp = p;
Uint i;
vp->outline = (int)AG_ReadUint8(ds);
vp->nPts = (Uint)AG_ReadUint8(ds);
vp->pts = Malloc(vp->nPts*sizeof(VG_Point *));
for (i = 0; i < vp->nPts; i++) {
if ((vp->pts[i] = VG_ReadRef(ds, vp, "Point")) == NULL)
return (-1);
}
return (0);
}
static void
Save(void *p, AG_DataSource *ds)
{
VG_Polygon *vp = p;
Uint i;
AG_WriteUint8(ds, (Uint8)vp->outline);
AG_WriteUint8(ds, (Uint8)vp->nPts);
for (i = 0; i < vp->nPts; i++)
VG_WriteRef(ds, vp->pts[i]);
}
static void
Destroy(void *p)
{
VG_Polygon *vp = p;
Free(vp->pts);
Free(vp->ints);
}
static int
CompareInts(const void *p1, const void *p2)
{
return (*(const int *)p1 - *(const int *)p2);
}
static void
DrawOutline(VG_Polygon *vp, VG_View *vv)
{
Uint32 c32 = VG_MapColorRGB(VGNODE(vp)->color);
int Ax, Ay, Bx, By, Cx, Cy;
int i;
if (vp->nPts < 2) {
return;
}
VG_GetViewCoords(vv, VG_Pos(vp->pts[0]), &Ax,&Ay);
Cx = Ax;
Cy = Ay;
for (i = 1; i < vp->nPts; i++) {
VG_GetViewCoords(vv, VG_Pos(vp->pts[i]), &Bx,&By);
AG_DrawLine(vv, Ax,Ay, Bx,By, c32);
Ax = Bx;
Ay = By;
}
AG_DrawLine(vv, Cx,Cy, Ax,Ay, c32);
}
static void
DrawFB(VG_Polygon *vp, VG_View *vv)
{
Uint32 c32 = VG_MapColorRGB(VGNODE(vp)->color);
int y, x1, y1, x2, y2;
int ign, miny, maxy;
int i, i1, i2;
int nInts;
if (vp->ints == NULL) {
vp->ints = Malloc(vp->nPts*sizeof(int));
vp->nInts = vp->nPts;
} else {
if (vp->nPts > vp->nInts) {
vp->ints = Realloc(vp->ints, vp->nPts*sizeof(int));
vp->nInts = vp->nPts;
}
}
/* Find Y maxima */
VG_GetViewCoords(vv, VG_Pos(vp->pts[0]), &ign, &miny);
maxy = miny;
for (i = 1; i < vp->nPts; i++) {
int vy;
VG_GetViewCoords(vv, VG_Pos(vp->pts[i]), &ign, &vy);
if (vy < miny) {
miny = vy;
} else if (vy > maxy) {
maxy = vy;
}
}
/* Find the intersections. */
for (y = miny; y <= maxy; y++) {
nInts = 0;
for (i = 0; i < vp->nPts; i++) {
if (i == 0) {
i1 = vp->nPts - 1;
i2 = 0;
} else {
i1 = i - 1;
i2 = i;
}
VG_GetViewCoords(vv, VG_Pos(vp->pts[i1]), &ign, &y1);
VG_GetViewCoords(vv, VG_Pos(vp->pts[i2]), &ign, &y2);
if (y1 < y2) {
VG_GetViewCoords(vv, VG_Pos(vp->pts[i1]),
&x1, &ign);
VG_GetViewCoords(vv, VG_Pos(vp->pts[i2]),
&x2, &ign);
} else if (y1 > y2) {
VG_GetViewCoords(vv, VG_Pos(vp->pts[i1]),
&x2, &y2);
VG_GetViewCoords(vv, VG_Pos(vp->pts[i2]),
&x1, &y1);
} else {
continue;
}
if (((y >= y1) && (y < y2)) ||
((y == maxy) && (y > y1) && (y <= y2))) {
vp->ints[nInts++] =
(((y-y1)<<16) / (y2-y1)) *
(x2-x1) + (x1<<16);
}
}
qsort(vp->ints, nInts, sizeof(int), CompareInts);
for (i = 0; i < nInts; i += 2) {
int xa, xb;
xa = vp->ints[i] + 1;
xa = (xa>>16) + ((xa&0x8000) >> 15);
xb = vp->ints[i+1] - 1;
xb = (xb>>16) + ((xb&0x8000) >> 15);
AG_DrawLineH(vv, xa, xb, y, c32);
}
}
}
static void
Draw(void *p, VG_View *vv)
{
VG_Polygon *vp = p;
int i;
if (vp->nPts < 3 || vp->outline) {
DrawOutline(vp, vv);
return;
}
#ifdef HAVE_OPENGL
if (agView->opengl) {
VG_Color *c = &VGNODE(vp)->color;
int x, y;
glBegin(GL_POLYGON);
glColor3ub(c->r, c->g, c->b);
for (i = 0; i < vp->nPts; i++) {
VG_GetViewCoords(vv, VG_Pos(vp->pts[i]), &x, &y);
x += WIDGET(vv)->rView.x1;
y += WIDGET(vv)->rView.y1;
glVertex2i(x, y);
}
glEnd();
} else
#endif /* HAVE_OPENGL */
{
DrawFB(vp, vv);
}
}
static void
Extent(void *p, VG_View *vv, VG_Vector *a, VG_Vector *b)
{
VG_Polygon *vp = p;
VG_Vector v;
int i;
if (vp->nPts < 1) {
a->x = b->x = 0;
a->y = b->y = 0;
return;
}
v = VG_Pos(vp->pts[0]);
a->x = b->x = v.x;
a->y = b->y = v.y;
for (i = 0; i < vp->nPts; i++) {
v = VG_Pos(vp->pts[i]);
if (v.x < a->x) { a->x = v.x; }
if (v.y < a->y) { a->y = v.y; }
if (v.x > b->x) { b->x = v.x; }
if (v.y > b->y) { b->y = v.y; }
}
}
static float
PointProximity(void *p, VG_View *vv, VG_Vector *vPt)
{
VG_Polygon *vp = p;
float d, dMin;
VG_Vector vInt, A, B, C, m;
int i;
if (vp->nPts < 1)
return (AG_FLT_MAX);
dMin = AG_FLT_MAX;
m.x = 0.0f;
m.y = 0.0f;
C = A = VG_Pos(vp->pts[0]);
for (i = 1; i < vp->nPts; i++) {
B = VG_Pos(vp->pts[i]);
vInt = *vPt;
d = VG_PointLineDistance(A, B, &vInt);
if (d < dMin) {
dMin = d;
m = vInt;
}
A = B;
}
vInt = *vPt;
d = VG_PointLineDistance(C, A, &vInt);
if (d < dMin) {
dMin = d;
m = vInt;
}
if (dMin < AG_FLT_MAX) {
vPt->x = m.x;
vPt->y = m.y;
}
return (dMin);
}
static void
Delete(void *p)
{
VG_Polygon *vp = p;
Uint i;
for (i = 0; i < vp->nPts; i++) {
if (VG_DelRef(vp, vp->pts[i]) == 0)
VG_Delete(vp->pts[i]);
}
}
static void *
Edit(void *p, VG_View *vv)
{
VG_Polygon *vp = p;
AG_Box *box = AG_BoxNewVert(NULL, AG_BOX_EXPAND);
AG_LabelNewPolled(box, AG_LABEL_HFILL, _("Points: %d"), &vp->nPts);
AG_SeparatorNewHoriz(box);
AG_CheckboxNewInt(box, 0, _("Render outline"), &vp->outline);
return (box);
}
VG_NodeOps vgPolygonOps = {
N_("Polygon"),
&vgIconPolygon,
sizeof(VG_Polygon),
Init,
Destroy,
Load,
Save,
Draw,
Extent,
PointProximity,
NULL, /* lineProximity */
Delete,
NULL, /* moveNode */
Edit
};