void on_move(struct bg_point * pos_p) { #ifndef NDEBUG printf("on_move() %d %d %d\n", pos_p->x, pos_p->y, state); #endif switch (state) { case STATE_LINE_WAITING_CLICK_SECOND_POINT: bg_undo_restore(); bg_draw_line(&from, pos_p); bg_draw_flush(); state = STATE_LINE_WAITING_CLICK_SECOND_POINT; break; case STATE_RECTANGLE_WAITING_CLICK_SECOND_POINT: bg_undo_restore(); bg_draw_rectangle(&from, pos_p); bg_draw_flush(); state = STATE_RECTANGLE_WAITING_CLICK_SECOND_POINT; case STATE_POLYLINE_WAITING_CLICK_SUBSEQUENT_POINT: bg_undo_restore(); bg_draw_polyline(&plist, pos_p); bg_draw_flush(); state = STATE_POLYLINE_WAITING_CLICK_SUBSEQUENT_POINT; break; case STATE_BEZIERCURVE_WAITING_CLICK_SUBSEQUENT_POINT: bg_undo_restore(); bg_draw_line_set_pattern(BG_LINE_PATTERN_3); /* 切换到虚线 1/8 */ bg_draw_polyline(&plist, pos_p); /* 画控制线 */ bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_beziercurve(&plist, pos_p); bg_draw_flush(); state = STATE_BEZIERCURVE_WAITING_CLICK_SUBSEQUENT_POINT; /* 指向自己的状态转移 */ break; case STATE_CIRCLE_WAITING_CLICK_POINT_ON_CIRCLE: bg_undo_restore(); bg_draw_circle( /* 画圆 */ ¢er_point, euclidian_distance(center_point, *pos_p)); bg_draw_line_set_pattern(BG_LINE_PATTERN_3); /* 切换到虚线 1/8 */ bg_draw_line(¢er_point, pos_p); bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_flush(); /* state 不变 */ break; } return; }
QPolygonF curvedArrow( QPointF po, QPointF pm, QPointF pd, qreal startWidth, qreal width, qreal headSize, QgsArrowSymbolLayer::HeadType headType, qreal offset ) { qreal circleRadius; QPointF circleCenter; if ( ! pointsToCircle( po, pm, pd, circleCenter, circleRadius ) || circleRadius > 10000.0 ) { // aligned points => draw a straight arrow return straightArrow( po, pd, startWidth, width, headSize, headType, offset ); } // angles of each point qreal angle_o = clampAngle( atan2( circleCenter.y() - po.y(), po.x() - circleCenter.x() ) ); qreal angle_m = clampAngle( atan2( circleCenter.y() - pm.y(), pm.x() - circleCenter.x() ) ); qreal angle_d = clampAngle( atan2( circleCenter.y() - pd.y(), pd.x() - circleCenter.x() ) ); // arc direction : 1 = counter-clockwise, -1 = clockwise int direction = clampAngle( angle_m - angle_o ) < clampAngle( angle_m - angle_d ) ? 1 : -1; qreal deltaAngle = angle_d - angle_o; if ( direction * deltaAngle < 0.0 ) deltaAngle = deltaAngle + direction * 2 * M_PI; qreal length = euclidian_distance( po, pd ); // for close points and deltaAngle < 180, draw a straight line if ( fabs( deltaAngle ) < M_PI && ((( headType == QgsArrowSymbolLayer::HeadSingle ) && ( length < headSize ) ) || (( headType == QgsArrowSymbolLayer::HeadReversed ) && ( length < headSize ) ) || (( headType == QgsArrowSymbolLayer::HeadDouble ) && ( length < 2*headSize ) ) ) ) { return straightArrow( po, pd, startWidth, width, headSize, headType, offset ); } // ajust coordinates to include offset circleRadius += offset; po = circlePoint( circleCenter, circleRadius, angle_o ); pm = circlePoint( circleCenter, circleRadius, angle_m ); pd = circlePoint( circleCenter, circleRadius, angle_d ); qreal headAngle = direction * atan( headSize / circleRadius ); QPainterPath path; if ( headType == QgsArrowSymbolLayer::HeadDouble ) { // the first head path.moveTo( po ); path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_o + headAngle ) ); pathArcTo( path, circleCenter, circleRadius + direction * width / 2, angle_o + headAngle, angle_d - headAngle, direction ); // the second head path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_d - headAngle ) ); path.lineTo( pd ); path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_d - headAngle ) ); pathArcTo( path, circleCenter, circleRadius - direction * width / 2, angle_d - headAngle, angle_o + headAngle, -direction ); // the end of the first head path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_o + headAngle ) ); path.lineTo( po ); } else if ( headType == QgsArrowSymbolLayer::HeadSingle ) { path.moveTo( circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) ); spiralArcTo( path, circleCenter, angle_o, circleRadius + direction * startWidth / 2, angle_d - headAngle, circleRadius + direction * width / 2, direction ); // the arrow head path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_d - headAngle ) ); path.lineTo( pd ); path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_d - headAngle ) ); spiralArcTo( path, circleCenter, angle_d - headAngle, circleRadius - direction * width / 2, angle_o, circleRadius - direction * startWidth / 2, -direction ); path.lineTo( circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) ); } else if ( headType == QgsArrowSymbolLayer::HeadReversed ) { path.moveTo( circlePoint( circleCenter, circleRadius + direction * width / 2, angle_o + headAngle ) ); spiralArcTo( path, circleCenter, angle_o + headAngle, circleRadius + direction * width / 2, angle_d, circleRadius + direction * startWidth / 2, direction ); path.lineTo( circlePoint( circleCenter, circleRadius - direction * startWidth / 2, angle_d ) ); spiralArcTo( path, circleCenter, angle_d, circleRadius - direction * startWidth / 2, angle_o + headAngle, circleRadius - direction * width / 2, - direction ); path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_o + headAngle ) ); path.lineTo( po ); path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_o + headAngle ) ); path.lineTo( circlePoint( circleCenter, circleRadius + direction * width / 2, angle_o + headAngle ) ); } return path.toSubpathPolygons().at( 0 ); }
QPolygonF straightArrow( QPointF po, QPointF pd, qreal startWidth, qreal width, qreal headSize, QgsArrowSymbolLayer::HeadType headType, qreal offset ) { QPolygonF polygon; // implicitly shared // vector length qreal length = euclidian_distance( po, pd ); // shift points if there is not enough room for the head(s) if (( headType == QgsArrowSymbolLayer::HeadSingle ) && ( length < headSize ) ) { po = pd - ( pd - po ) / length * headSize; length = headSize; } else if (( headType == QgsArrowSymbolLayer::HeadReversed ) && ( length < headSize ) ) { pd = po + ( pd - po ) / length * headSize; length = headSize; } else if (( headType == QgsArrowSymbolLayer::HeadDouble ) && ( length < 2 * headSize ) ) { QPointF v = ( pd - po ) / length * headSize; QPointF npo = ( po + pd ) / 2.0 - v; QPointF npd = ( po + pd ) / 2.0 + v; po = npo; pd = npd; length = 2 * headSize; } qreal bodyLength = length - headSize; // unit vector QPointF unitVec = ( pd - po ) / length; // perpendicular vector QPointF perpVec( -unitVec.y(), unitVec.x() ); // set offset po += perpVec * offset; pd += perpVec * offset; if ( headType == QgsArrowSymbolLayer::HeadDouble ) { // first head polygon << po; polygon << po + unitVec * headSize + perpVec * headSize; polygon << po + unitVec * headSize + perpVec * ( width * 0.5 ); polygon << po + unitVec * bodyLength + perpVec * ( width * 0.5 ); // second head polygon << po + unitVec * bodyLength + perpVec * headSize; polygon << pd; polygon << po + unitVec * bodyLength - perpVec * headSize; polygon << po + unitVec * bodyLength - perpVec * ( width * 0.5 ); // end of the first head polygon << po + unitVec * headSize - perpVec * ( width * 0.5 ); polygon << po + unitVec * headSize - perpVec * headSize; } else if ( headType == QgsArrowSymbolLayer::HeadSingle ) { polygon << po - perpVec * ( startWidth * 0.5 ); polygon << po + perpVec * ( startWidth * 0.5 ); polygon << po + unitVec * bodyLength + perpVec * ( width * 0.5 ); polygon << po + unitVec * bodyLength + perpVec * headSize; polygon << pd; polygon << po + unitVec * bodyLength - perpVec * headSize; polygon << po + unitVec * bodyLength - perpVec * ( width * 0.5 ); } else if ( headType == QgsArrowSymbolLayer::HeadReversed ) { // first head polygon << po; polygon << po + unitVec * headSize + perpVec * headSize; polygon << po + unitVec * headSize + perpVec * ( width * 0.5 ); polygon << pd + perpVec * ( startWidth * 0.5 ); polygon << pd - perpVec * ( startWidth * 0.5 ); polygon << po + unitVec * headSize - perpVec * ( width * 0.5 ); polygon << po + unitVec * headSize - perpVec * headSize; } // close the polygon polygon << polygon.first(); return polygon; }
void on_drag(struct bg_point * pos_p) { #ifndef NDEBUG printf("on_drag() %d %d %d\n", pos_p->x, pos_p->y, state); #endif switch (state) { case STATE_LINE_WAITING_RELEASE_FIRST_POINT: case STATE_LINE_WAITING_RELEASE_SECOND_POINT: bg_undo_restore(); bg_draw_line(&from, pos_p); bg_draw_flush(); state = STATE_LINE_WAITING_RELEASE_SECOND_POINT; break; case STATE_LINE_WAITING_CLICK_SECOND_POINT: bg_undo_restore(); bg_draw_line(&from, pos_p); bg_draw_flush(); state = STATE_LINE_WAITING_RELEASE_SECOND_POINT; break; case STATE_RECTANGLE_WAITING_RELEASE_FIRST_POINT: case STATE_RECTANGLE_WAITING_RELEASE_SECOND_POINT: bg_undo_restore(); bg_draw_rectangle(&from, pos_p); bg_draw_flush(); state = STATE_RECTANGLE_WAITING_RELEASE_SECOND_POINT; break; case STATE_RECTANGLE_WAITING_CLICK_SECOND_POINT: bg_undo_restore(); bg_draw_rectangle(&from, pos_p); bg_draw_flush(); state = STATE_RECTANGLE_WAITING_RELEASE_SECOND_POINT; break; case STATE_CIRCLE_WAITING_RELEASE_CENTER_POINT: center_point = *pos_p; state = STATE_CIRCLE_WAITING_RELEASE_POINT_ON_CIRCLE; break; case STATE_CIRCLE_WAITING_RELEASE_POINT_ON_CIRCLE: bg_undo_restore(); bg_draw_circle( /* 画圆 */ ¢er_point, euclidian_distance(center_point, *pos_p)); bg_draw_line_set_pattern(BG_LINE_PATTERN_3); /* 切换到虚线 1/8 */ bg_draw_line(¢er_point, pos_p); /* 辅助线 */ bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_flush(); /* state 不变 */ break; case STATE_SELECTION_WAITING_RELEASE_POINT: bg_undo_restore(); bg_draw_line_set_pattern(BG_LINE_PATTERN_2); /* 切换到虚线 1/4 */ bg_draw_rectangle(&from, pos_p); bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_flush(); /* state 不变 */ break; case STATE_ROTATE_WAITING_RELEASE: bg_undo_restore(); /* 为了避免辅助线被“刷白”的选区遮挡,要后画辅助线 */ bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_rotate( &selection_from, &selection_to, &from, pos_p); bg_draw_line_set_pattern(BG_LINE_PATTERN_3); /* 切换到虚线 1/4 */ bg_draw_line(&from, pos_p); /* 辅助线 */ bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_flush(); /* state 不变 */ return; case STATE_TRANSFORM_WAITING_RELEASE: bg_undo_restore(); /* 为了避免辅助线被“刷白”的选区遮挡,要后画辅助线 */ bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ tmp.x = pos_p->x - from.x; /* 使用点 tmp 当作临时变量来表示移动向量 */ tmp.y = pos_p->y - from.y; bg_draw_transform(&selection_from, &selection_to, &tmp); bg_draw_line_set_pattern(BG_LINE_PATTERN_3); /* 切换到虚线 1/4 */ bg_draw_line(&from, pos_p); /* 辅助线 */ bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_flush(); /* state 不变 */ } return; }
void on_release(struct bg_point * pos_p) { #ifndef NDEBUG printf("on_release() %d %d %d\n", pos_p->x, pos_p->y, state); #endif /* NDEBUG */ /* for SELECTION in switch */ int minX, maxX; int minY, maxY; switch (state) { case STATE_LINE_WAITING_RELEASE_FIRST_POINT: state = STATE_LINE_WAITING_CLICK_SECOND_POINT; break; case STATE_LINE_WAITING_RELEASE_SECOND_POINT: bg_undo_restore(); bg_draw_line(&from, pos_p); bg_undo_commit(); bg_draw_flush(); state = STATE_LINE_WAITING_CLICK_FIRST_POINT; break; case STATE_RECTANGLE_WAITING_RELEASE_FIRST_POINT: state = STATE_RECTANGLE_WAITING_CLICK_SECOND_POINT; break; case STATE_RECTANGLE_WAITING_RELEASE_SECOND_POINT: bg_undo_restore(); bg_draw_rectangle(&from, pos_p); bg_undo_commit(); bg_draw_flush(); state = STATE_RECTANGLE_WAITING_CLICK_FIRST_POINT; break; case STATE_POLYLINE_WAITING_RELEASE_FIRST_POINT: state = STATE_POLYLINE_WAITING_CLICK_SUBSEQUENT_POINT; break; case STATE_POLYLINE_WAITING_RELEASE_SUBSEQUENT_POINT: bg_undo_restore(); bg_draw_polyline(&plist, pos_p); bg_draw_flush(); state = STATE_POLYLINE_WAITING_CLICK_SUBSEQUENT_POINT; break; case STATE_BEZIERCURVE_WAITING_RELEASE_FIRST_POINT: state = STATE_BEZIERCURVE_WAITING_CLICK_SUBSEQUENT_POINT; break; case STATE_BEZIERCURVE_WAITING_RELEASE_SUBSEQUENT_POINT: bg_undo_restore(); bg_draw_line_set_pattern(BG_LINE_PATTERN_3); /* 切换到虚线 1/8 */ bg_draw_polyline(&plist, pos_p); /* 画控制线 */ bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_beziercurve(&plist, pos_p); bg_draw_flush(); state = STATE_BEZIERCURVE_WAITING_CLICK_SUBSEQUENT_POINT; break; case STATE_CIRCLE_WAITING_RELEASE_POINT_ON_CIRCLE: bg_undo_restore(); bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_circle( /* 画圆 */ ¢er_point, euclidian_distance(center_point, *pos_p)); bg_undo_commit(); /* 提交更改 */ bg_draw_flush(); state = STATE_CIRCLE_WAITING_CLICK_CENTER_POINT; break; case STATE_CIRCLE_WAITING_RELEASE_CENTER_POINT: center_point = *pos_p; state = STATE_CIRCLE_WAITING_CLICK_POINT_ON_CIRCLE; break; case STATE_SELECTION_WAITING_RELEASE_POINT: #define MIN(a, b) ((a)>(b)?(b):(a)) #define MAX(a, b) ((a)>(b)?(a):(b)) selection_from = from; selection_to = *pos_p; minX = MIN(selection_from.x, selection_to.x); maxX = MAX(selection_from.x, selection_to.x); minY = MIN(selection_from.y, selection_to.y); maxY = MAX(selection_from.y, selection_to.y); selection_from.x = minX; selection_from.y = minY; selection_to.x = maxX; selection_to.y = maxY; #undef MIN #undef MAX state = STATE_SELECTION_WAITING_CLICK_POINT; break; case STATE_ROTATE_WAITING_RELEASE: bg_undo_restore(); bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ bg_draw_rotate( &selection_from, &selection_to, &from, pos_p); bg_undo_commit(); bg_draw_flush(); selection_from.x = selection_from.y = 400; selection_to.x = selection_to.y = 400; break; case STATE_TRANSFORM_WAITING_RELEASE: bg_undo_restore(); bg_draw_line_set_pattern(BG_LINE_PATTERN_0); /* 切换到实线 */ tmp.x = pos_p->x - from.x; /* 使用点 tmp 当作临时变量来表示移动向量 */ tmp.y = pos_p->y - from.y; bg_draw_transform(&selection_from, &selection_to, &tmp); bg_undo_commit(); bg_draw_flush(); selection_from.x += tmp.x; selection_from.y += tmp.y; selection_to.x += tmp.x; selection_to.y += tmp.y; state = STATE_TRANSFORM_WAITING_PRESS; break; } return; }