You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

702 lines
21 KiB

/***************************************************************************
qsdrv.cpp
-------------------
begin : 01-January-2000
copyright : (C) 2000 by Kamil Dobkowski
email : kamildobk@poczta.onet.pl
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qsdrv.h"
#include <qwmatrix.h>
#include <math.h>
#include <iostream>
//-------------------------------------------------------------//
QSCanvasDrv::QSCanvasDrv()
{
dpi = 72.0;
}
//-------------------------------------------------------------//
QSCanvasDrv::~QSCanvasDrv()
{
stopDrawing();
}
//-------------------------------------------------------------//
void QSCanvasDrv::startDrawing()
{
}
//-------------------------------------------------------------//
void QSCanvasDrv::stopDrawing()
{
}
//-------------------------------------------------------------//
void QSCanvasDrv::drawPoint( const QSPt2f& pos, const QSGPoint& point )
{
if ( point.style != QSGPoint::Invisible ) {
double size = toPixels(point.size);
QSPt2f p1( pos.x - size/2.0, pos.y - size/2.0 );
QSPt2f p2( p1.x + size, p1.y + size );
QSGLine cl = currentLine();
QSGFill cf = currentFill();
QSGLine l; l.color = point.color; l.width = int(floor(size/15.0+0.5));
QSGFill f;
if ( point.fill == QSGPoint::Filled ) f.color = point.color;
if ( point.fill == QSGPoint::Transparent ) f.style = QSGFill::Transparent;
if ( cf != f ) setFill( f );
if ( cl != l ) setLine( l );
QSPt2f p[4];
switch( point.style ) {
case QSGPoint::Circle:
drawEllipse(p1,p2);
break;
case QSGPoint::Rect:
drawRect( p1, p2 );
break;
case QSGPoint::Triangle
: p[0].set( pos.x, p1.y );
p[1].set( p2.x, p2.y );
p[2].set( p1.x, p2.y );
drawPoly( p, 3 );
break;
case QSGPoint::Diamond:
p[0].set( pos.x, p1.y );
p[1].set( p2.x, pos.y );
p[2].set( pos.x, p2.y );
p[3].set( p1.x, pos.y );
drawPoly( p, 4 );
break;
case QSGPoint::Cross:
drawLine( p1, p2 );
drawLine( p[0].set(p2.x,p1.y), p[1].set(p1.x,p2.y) );
break;
case QSGPoint::Plus:
drawLine( p[0].set(p1.x,pos.y), p[1].set(p2.x,pos.y) );
drawLine( p[0].set(pos.x,p1.y), p[1].set(pos.x,p2.y) );
break;
case QSGPoint::HLine:
drawLine( p[0].set(p1.x,pos.y), p[1].set(p2.x,pos.y) );
break;
case QSGPoint::VLine:
drawLine( p[0].set(pos.x,p1.y), p[1].set(pos.x,p2.y) );
break;
default: break;
}
}
}
//-------------------------------------------------------------//
/* { number_of_vertices, x1, y1, x2, y2, ... } */
// arrow
static const double arrow_A[] = { 4, 0, 0, -2, -1, -1, 0, -2, 1 };
// filled arrow
static const double arrow_FA[] = { 3, 0, 0, -2, -1, -2, 1 };
// narrow arrow
static const double arrow_NA[] = { 3, 0, 0, -3, -1, -3, 1 };
// reversed arrow
static const double arrow_RA[] = { 4, 0, 0, 2, -1, 1, 0, 2, 1 };
// reversed filled arrow
static const double arrow_RFA[] = { 3, 0, 0, 2, -1, 2, 1 };
// reversed narrow arrow
static const double arrow_RNA[] = { 3, 0, 0, 3, -1, 3, 1 };
// rectangle
static const double arrow_R[] = { 4, -1, -1, 1, -1, 1, 1, -1, 1 };
// diamond
static const double arrow_D[] = { 4, -1, 0, 0, -1, 1, 0, 0, 1 };
// line
static const double arrow_L[] = { 2, 0, -1, 0, 1 };
// fdiag line
static const double arrow_F[] = { 2, -1,-1, 1, 1 };
// bdiag line
static const double arrow_B[] = { 2, -1, 1, 1, -1 };
static const double *arrows[] = { NULL,
arrow_A,
arrow_FA,
arrow_NA,
arrow_RA,
arrow_RFA,
arrow_RNA,
arrow_R,
arrow_D,
NULL,
arrow_L,
arrow_F,
arrow_B };
//-------------------------------------------------------------//
void QSCanvasDrv::drawDart( const QSPt2f& pos, double angle, const QSGArrow& arrow )
{
if ( arrow.style == QSGArrow::None ) return;
double scale = toPixels( arrow.size );
QSGLine cl = currentLine();
QSGLine l; l.color = cl.color; setLine( l );
QSGFill f; f.color = cl.color; setFill( f );
if ( arrow.style == QSGArrow::Circle ) {
double size = scale;;
QSPt2f p1( pos.x - size, pos.y - size );
QSPt2f p2( pos.x + size, pos.y + size );
drawEllipse( p1, p2 );
return;
}
QSPt2f p[4]; QWMatrix m;
m.translate( pos.x, pos.y ); m.rotate( angle ); m.scale( scale, scale );
for( int i=0; i<arrows[arrow.style][0]; i++ ) m.map( arrows[arrow.style][i*2+1],
arrows[arrow.style][i*2+2],
&p[i].x, &p[i].y );
if (arrows[arrow.style][0] > 2 ) drawPoly( p, (int )arrows[arrow.style][0] );
else drawLine( p[0], p[1] );
}
//-------------------------------------------------------------//
void QSCanvasDrv::drawArrow( const QSPt2f& p1, const QSPt2f& p2, const QSGArrow& p1style, const QSGArrow& p2style )
{
double angle = ( p1 == p2 ) ? 0.0 : atan2(p2.y-p1.y,p2.x-p1.x)*180.0/3.141592;
drawLine( p1, p2 );
drawDart( p1, angle+180.0, p1style );
drawDart( p2, angle+ 0.0, p2style );
}
//-------------------------------------------------------------//
void QSCanvasDrv::drawRTextBox( const QSPt2f &pos, int angle, const QString& text, int align )
{
QSPt2f text_size = rTextSize( angle, text );
QSPt2f text_pos = pos;
if ( align & AlignLeft ) text_pos.x += text_size.x/2.0;
if ( align & AlignRight ) text_pos.x -= text_size.x/2.0;
if ( align & AlignTop ) text_pos.y += text_size.y/2.0;
if ( align & AlignBottom ) text_pos.y -= text_size.y/2.0;
drawRText( text_pos, angle, text, AlignVCenter | AlignHCenter );
}
//-------------------------------------------------------------//
QSPt2f QSCanvasDrv::rTextSize( int angle, const QString& text )
{
QSPt2f pts[4];
getRTextBoundingPoly( pts, QSPt2f(0,0), angle, text );
QSPt2f min;
QSPt2f max;
min.x = QMIN(pts[0].x,pts[1].x); min.x = QMIN(min.x,pts[2].x); min.x = QMIN(min.x,pts[3].x);
min.y = QMIN(pts[0].y,pts[1].y); min.y = QMIN(min.y,pts[2].y); min.y = QMIN(min.y,pts[3].y);
max.x = QMAX(pts[0].x,pts[1].x); max.x = QMAX(max.x,pts[2].x); max.x = QMAX(max.x,pts[3].x);
max.y = QMAX(pts[0].y,pts[1].y); max.y = QMAX(max.y,pts[2].y); max.y = QMAX(max.y,pts[3].y);
return QSPt2f( max.x-min.x+1.0, max.y-min.y+1.0 );
}
//-------------------------------------------------------------//
/*
void QSCanvasDrv::drawRText( const QSPt2f&, int, const QString&, int )
{
}
//-------------------------------------------------------------//
void QSCanvasDrv::getRTextBoundingPoly( QSPt2f [4], const QSPt2f&, int, const QString&, int )
{
}
//-------------------------------------------------------------//
void QSCanvasDrv::beginPolyline( const QSPt2f& )
{
}
//-------------------------------------------------------------//
void QSCanvasDrv::drawPolylineTo( const QSPt2f& )
{
}
*/
//-------------------------------------------------------------//
void QSCanvasDrv::getPixmapBuffer( PixmapBuffer *buff, int, int )
{
buff->ptr = NULL;
}
//-------------------------------------------------------------//
void QSCanvasDrv::drawPixmap( const QSPt2f&, PixmapBuffer * )
{
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
QSDrv::QSDrv()
:QSCanvasDrv()
{
m_t = NULL;
m_clipping = false;
m_pts = NULL;
m_cpts2 = NULL;
m_cpts3 = NULL;
m_cedges = NULL;
m_ncpts2 = 0;
m_ncpts3 = 0;
m_max_cedges = 0;
m_max_pts = 0;
m_max_cpts2 = 0;
m_max_cpts3 = 0;
m_top_bottom = false;
m_category = -1;
m_element = -1;
}
//-------------------------------------------------------------//
QSDrv::~QSDrv()
{
}
//-------------------------------------------------------------//
void QSDrv::setProjection( const QSProjection *t )
{
m_t = t;
}
//-------------------------------------------------------------//
void QSDrv::setCurrentElement( int category, int element )
{
m_category = category;
m_element = element;
}
//-------------------------------------------------------------//
void QSDrv::setClipping( bool enabled )
{
m_clipping = enabled;
}
//-------------------------------------------------------------//
void QSDrv::setTopBottom( bool enabled )
{
m_top_bottom = enabled;
}
//-------------------------------------------------------------//
void QSDrv::setBottomFill( const QSGFill& f )
{
m_bottom_fill = f;
}
//-------------------------------------------------------------//
void QSDrv::startDrawing()
{
QSCanvasDrv::startDrawing();
}
//-------------------------------------------------------------//
void QSDrv::stopDrawing()
{
delete[] m_pts; m_pts = NULL; m_max_pts = 0;
delete[] m_cpts2; m_cpts2 = NULL; m_ncpts2 = 0; m_max_cpts2 = 0;
delete[] m_cpts3; m_cpts3 = NULL; m_ncpts3 = 0; m_max_cpts3 = 0;
m_category = -1;
m_element = -1;
QSCanvasDrv::stopDrawing();
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
void QSDrv::drawPoly3( const QSPt3f pts3[], int npoints, const QSPt3f *norm, const QSGFill *fills, const bool edges[], int edgeAutoColor )
// if driver uses something else except norm[0], colors[0] you must
// reimplement this function to clipVertexColors and clipVertexNormals
{
const bool *in_edges = NULL;
const QSPt3f *in_pts3 = NULL;
int in_npts3 = 0;
if ( m_clipping ) {
QSProjection::ClipResult clip = clip_poly( pts3, npoints, edges );
if ( clip == QSProjection::Accepted ) { in_pts3 = pts3; in_npts3 = npoints; in_edges = edges; }
else
if ( clip == QSProjection::Clipped ) { in_pts3 = m_cpts3; in_npts3 = m_ncpts3; in_edges = m_cedges; }
else
if ( clip == QSProjection::Rejected ) return;
} else {
in_pts3 = pts3; in_npts3 = npoints;
}
map_to_screen( in_pts3, in_npts3 );
// if ( m_stage > 0 ) {
QSGFill f = fills[0];
m_t->shade( f, norm[0], m_pts, in_npts3, (m_top_bottom?&m_bottom_fill:NULL) );
setFill( f );
drawPoly( m_pts, in_npts3, in_edges, edgeAutoColor );
}
/*
if ( cNormals() == VertexNormals || 1 ) {
QSPt3f no_norm[2];
for( int i=0; i<npoints; i++ ) {
QSPt3f n( norm[i+1].x/5.0, norm[i+1].y/5.0, norm[i+1].z/5.0 );
drawLine3( pts3[i], pts3[i]+n, no_norm );
}
}
*/
//-------------------------------------------------------------//
void QSDrv::drawLine3( const QSPt3f& pos1, const QSPt3f& pos2, const QSPt3f [2] )
{
if ( m_clipping ) {
QSPt3f p1 = pos1; QSPt3f p2 = pos2;
if ( m_t->clipLine3(&p1,&p2) ) drawLine( m_t->world3DToCanvas(p1), m_t->world3DToCanvas(p2) );
} else {
drawLine( m_t->world3DToCanvas(pos1), m_t->world3DToCanvas(pos2) );
}
}
//-------------------------------------------------------------//
void QSDrv::drawText3( const QSPt3f& pos, const QString& text, int align )
{
if ( !m_clipping || (m_clipping && m_t->clipPoint3(pos)) )
drawText( m_t->world3DToCanvas(pos), text, align );
}
//-------------------------------------------------------------//
void QSDrv::drawPoint3( const QSPt3f& pos, const QSGPoint& point )
{
if ( !m_clipping || (m_clipping && m_t->clipPoint3(pos)) )
drawPoint( m_t->world3DToCanvas(pos), point );
}
//-------------------------------------------------------------//
void QSDrv::clearCanvas( const QSGFill&, const QSPt2f&, const QSPt2f& )
{
}
//-------------------------------------------------------------//
void QSDrv::drawLine2( const QSPt2f &one, const QSPt2f &two )
{
if ( m_clipping ) {
QSPt2f p1 = one;
QSPt2f p2 = two;
if ( m_t->clipLine2(&p1,&p2) ) drawLine( m_t->world2DToCanvas(p1), m_t->world2DToCanvas(p2) );
} else {
drawLine( m_t->world2DToCanvas(one), m_t->world2DToCanvas(two) );
}
}
//-------------------------------------------------------------//
void QSDrv::drawRect2( const QSPt2f &p1, const QSPt2f &p2 )
{
if ( !m_clipping || (m_clipping && (m_t->clipPoint2(p1) || m_t->clipPoint2(p2))) )
drawRect( m_t->world2DToCanvas(p1), m_t->world2DToCanvas(p2) );
}
//-------------------------------------------------------------//
void QSDrv::drawPoly2( const QSPt2f pts[], int npoints, const bool edges[], int edgeAutoColor )
{
if ( m_clipping ) {
QSProjection::ClipResult clip = clip_poly( pts, npoints, edges );
if ( clip == QSProjection::Accepted ) {
map_to_screen( pts, npoints );
drawPoly( m_pts, npoints, edges, edgeAutoColor );
}
else
if ( clip == QSProjection::Clipped ) {
map_to_screen( m_cpts2, m_ncpts2 );
drawPoly( m_pts, m_ncpts2, m_cedges, edgeAutoColor );
}
} else {
map_to_screen( pts, npoints );
drawPoly( m_pts, npoints, edges, edgeAutoColor );
}
}
//-------------------------------------------------------------//
void QSDrv::drawEllipse2( const QSPt2f& p1, const QSPt2f& p2 )
{
if ( !m_clipping || (m_clipping && (m_t->clipPoint2(p1) || m_t->clipPoint2(p2))) )
drawEllipse( m_t->world2DToCanvas(p1), m_t->world2DToCanvas(p2) );
}
//-------------------------------------------------------------//
void QSDrv::drawText2( const QSPt2f &pos, const QString& text, int align )
{
if ( !m_clipping || (m_clipping && m_t->clipPoint2(pos)) )
drawText( m_t->world2DToCanvas(pos), text, align );
}
//-------------------------------------------------------------//
void QSDrv::drawRText2( const QSPt2f &pos, int angle, const QString& text, int align )
{
if ( !m_clipping || (m_clipping && m_t->clipPoint2(pos)) )
drawRText( m_t->world2DToCanvas(pos), angle, text, align );
}
//-------------------------------------------------------------//
void QSDrv::drawRTextBox2( const QSPt2f &pos, int angle, const QString& text, int align )
{
if ( !m_clipping || (m_clipping && m_t->clipPoint2(pos)) )
drawRTextBox( m_t->world2DToCanvas(pos), angle, text, align );
}
//-------------------------------------------------------------//
void QSDrv::drawPoint2( const QSPt2f& pos, const QSGPoint& style )
{
if ( !m_clipping || (m_clipping && m_t->clipPoint2(pos)) )
drawPoint( m_t->world2DToCanvas(pos), style );
}
//-------------------------------------------------------------//
void QSDrv::drawDart2( const QSPt2f& pos, double angle, const QSGArrow& style )
{
drawDart( m_t->world2DToCanvas(pos), angle, style );
}
//-------------------------------------------------------------//
void QSDrv::drawArrow2( const QSPt2f& pos1, const QSPt2f& pos2, const QSGArrow& p1style, const QSGArrow& p2style )
{
if ( m_clipping ) {
QSPt2f p1 = pos1;
QSPt2f p2 = pos2;
QSGArrow s1 = p1style;
QSGArrow s2 = p2style;
if (m_t->clipLine2(&p1,&p2)) {
if ( p1 != pos1 ) s1.style = QSGArrow::None;
if ( p2 != pos2 ) s2.style = QSGArrow::None;
drawArrow( m_t->world2DToCanvas(p1), m_t->world2DToCanvas(p2), s1, s2 );
}
} else {
drawArrow( m_t->world2DToCanvas(pos1), m_t->world2DToCanvas(pos2), p1style, p2style );
}
}
//-------------------------------------------------------------//
void QSDrv::beginPolyline2( const QSPt2f& pos )
{
m_curr_polyline_pos = pos;
beginPolyline( m_t->world2DToCanvas(pos) );
}
//-------------------------------------------------------------//
void QSDrv::drawPolylineTo2( const QSPt2f& pos )
{
if ( m_clipping ) {
QSPt2f p1 = m_curr_polyline_pos;
QSPt2f p2 = pos;
if (m_t->clipLine2(&p1,&p2)) {
QSPt2f canvas_p1 = m_t->world2DToCanvas(p1);
QSPt2f canvas_p2 = m_t->world2DToCanvas(p2);
if (p1!=m_curr_polyline_pos) {
endPolyline();
beginPolyline(canvas_p1);
}
// leave a place for a label
QSPt2f label_p1 = canvas_p1;
QSPt2f label_p2 = canvas_p2;
QSProjection::ClipResult clip_result = m_t->clipLine( &label_p1, &label_p2, m_polyline_label_pos, m_polyline_label_size );
// skip
if ( clip_result == QSProjection::Accepted ) {
endPolyline();
beginPolyline( canvas_p2 );
}
else
// draw normal line
if ( clip_result == QSProjection::Rejected ) {
drawPolylineTo( canvas_p2 );
}
else
// draw clipped line
if ( clip_result == QSProjection::Clipped ) {
if ( canvas_p1 != label_p1 &&
canvas_p2 != label_p2 ) {
drawPolylineTo( label_p1 );
endPolyline(); beginPolyline( label_p2 );
drawPolylineTo( canvas_p2 );
}
else
if ( canvas_p1 != label_p1 ) {
drawPolylineTo( label_p1 );
}
else
if ( canvas_p2 != label_p2 ) {
endPolyline(); beginPolyline( label_p2 );
drawPolylineTo( canvas_p2 );
}
}
}
} else {
drawPolylineTo( m_t->world2DToCanvas(pos) );
}
m_curr_polyline_pos = pos;
}
//-------------------------------------------------------------//
void QSDrv::endPolyline2()
{
endPolyline();
}
//-------------------------------------------------------------//
QSPt2f QSDrv::currPolylinePos2()
{
return m_curr_polyline_pos;
}
//-------------------------------------------------------------//
void QSDrv::setPolylineLabelPlace2( const QString& label, const QSPt2f& label_place, int angle )
{
if ( !label.isEmpty() ) {
QSPt2f size = rTextSize( angle, label );
m_polyline_label_size = size;
m_polyline_label_pos = m_t->world2DToCanvas( label_place );
m_polyline_label_pos.x -= size.x/2.0;
m_polyline_label_pos.y -= size.y/2.0;
} else {
m_polyline_label_size = QSPt2f();
}
}
//-------------------------------------------------------------//
QSProjection::ClipResult QSDrv::clip_poly( const QSPt2f *pts, int npoints, const bool edges[] )
{
int clip_buff_size = npoints<<2;
if ( clip_buff_size > m_max_cpts2 ) { delete[] m_cpts2; m_max_cpts2 = clip_buff_size; m_cpts2 = new QSPt2f[m_max_cpts2]; }
if ( clip_buff_size > m_max_cedges ) { delete[] m_cedges; m_max_cedges = clip_buff_size; m_cedges = new bool[m_max_cedges]; }
return m_t->clipPoly2( pts, npoints, m_cpts2, &m_ncpts2, m_max_cpts2, m_cedges, edges );
}
//-------------------------------------------------------------//
QSProjection::ClipResult QSDrv::clip_poly( const QSPt3f *pts, int npoints, const bool edges[] )
{
int clip_buff_size = npoints<<2;
if ( clip_buff_size > m_max_cpts3 ) { delete[] m_cpts3; m_max_cpts3 = clip_buff_size; m_cpts3 = new QSPt3f[m_max_cpts3]; }
if ( clip_buff_size > m_max_cedges ) { delete[] m_cedges; m_max_cedges = clip_buff_size; m_cedges = new bool[m_max_cedges]; }
QSPt3f bbox[2]; m_t->getPoly3Cube( pts, npoints, bbox );
return m_t->clipPoly3( pts, npoints, m_cpts3, &m_ncpts3, m_max_cpts3, bbox, m_cedges, edges );
}
//-------------------------------------------------------------//
void QSDrv::map_to_screen( const QSPt2f *pts, int npoints )
{
if ( npoints > m_max_pts ) { delete[] m_pts; m_max_pts = npoints; m_pts = new QSPt2f[m_max_pts]; }
for( int i=0; i<npoints; i++ ) m_pts[i] = m_t->world2DToCanvas( pts[i] );
}
//-------------------------------------------------------------//
void QSDrv::map_to_screen( const QSPt3f *pts, int npoints )
{
if ( npoints > m_max_pts ) { delete[] m_pts; m_max_pts = npoints; m_pts = new QSPt2f[m_max_pts]; }
for( int i=0; i<npoints; i++ ) m_pts[i] = m_t->world3DToCanvas( pts[i] );
}
//-------------------------------------------------------------//
void QSDrv::copySettingsFrom( const QSDrv *drv )
{
setProjection( drv->projection() );
}
/**
* Converts points (1/72 inch) to pixels - "pixels = points*dpi/72".
* All fixed sizes must be converted by this function.
* There must not be fixed pixel-sizes !
* ( all legend sizes, spaces, shadows shifts etc must have its fixed size in points )
*/
//inline int toPixels( int points ) { return int(QSCoord::pointsToPixels(points,dpi)+0.5); }
/*
//if ( cl != l ) setLine( cl );
//if ( cf != f ) setFill( cf );
//if ( cf != f ) setFill( cf );
//if ( cl != l ) setLine( cl );
//if ( cl != l ) setLine( l );
//if ( cf != f ) setFill( f );
// QSGFill cf = currentFill();
*/