* added a framework for affine transformations and manipulating them

- Transformable: the base class with a nice interface to agg::trans_affine
- ChannelTransform: inheriting from Transformable, keeping the affine
  parameters separate
- TransformBox: inheriting from ChannelTransform and Manipulator
- TransformShapesBox: transfering the TransformBox transformation onto
  multiple selected Shape objects
* Shape inherits from Transformable
* solved an important TODO in IconRenderer: a Gradient is now transformed
  along with a Shape
TODO: Undo/Redo for manipulating the transformation


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18099 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2006-07-11 17:25:37 +00:00
parent ea3746798b
commit 0e684ccfed
17 changed files with 2748 additions and 31 deletions

View File

@ -26,6 +26,7 @@ local sourceDirs =
shape
shape/commands
style
transformable
transformer
;
@ -138,6 +139,13 @@ Application Icon-O-Matic :
SetGradientCommand.cpp
Style.cpp
StyleManager.cpp
# transformable
ChannelTransform.cpp
Transformable.cpp
TransformBox.cpp
TransformBoxStates.cpp
TransformCommand.cpp
TransformShapesBox.cpp
# transformer
AffineTransformer.cpp
ContourTransformer.cpp

View File

@ -31,6 +31,7 @@
#include "SwatchGroup.h"
#include "TransformerFactory.h"
#include "TransformerListView.h"
#include "TransformShapesBox.h"
// TODO: just for testing
#include "AffineTransformer.h"
@ -144,6 +145,25 @@ case MSG_SHAPE_SELECTED: {
fPathListView->SetCurrentShape(shape);
fStyleListView->SetCurrentShape(shape);
fTransformerListView->SetShape(shape);
BList selectedShapes;
ShapeContainer* shapes = fDocument->Icon()->Shapes();
int32 count = shapes->CountShapes();
for (int32 i = 0; i < count; i++) {
shape = shapes->ShapeAtFast(i);
if (shape->IsSelected()) {
selectedShapes.AddItem((void*)shape);
}
}
if (selectedShapes.CountItems() > 0) {
TransformShapesBox* transformBox = new TransformShapesBox(
fCanvasView,
(const Shape**)selectedShapes.Items(),
selectedShapes.CountItems());
fState->DeleteManipulators();
fState->AddManipulator(transformBox);
}
break;
}

View File

@ -27,6 +27,11 @@
using std::nothrow;
class StyleHandler {
struct StyleItem {
Style* style;
Transformation transformation;
};
public:
StyleHandler(GammaTable& gammaTable)
: fStyles(20),
@ -35,13 +40,20 @@ class StyleHandler {
fColor(0, 0, 0, 0)
{}
~StyleHandler()
{
int32 count = fStyles.CountItems();
for (int32 i = 0; i < count; i++)
delete (StyleItem*)fStyles.ItemAtFast(i);
}
bool is_solid(unsigned styleIndex) const
{
Style* style = (Style*)fStyles.ItemAt(styleIndex);
if (!style)
StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
if (!styleItem)
return true;
return style->Gradient() == NULL;
return styleItem->style->Gradient() == NULL;
}
const agg::rgba8& color(unsigned styleIndex);
@ -49,9 +61,21 @@ class StyleHandler {
void generate_span(agg::rgba8* span, int x, int y,
unsigned len, unsigned styleIndex);
bool AddStyle(Style* style)
bool AddStyle(Style* style, const Transformation& transformation)
{
return fStyles.AddItem((void*)style);
if (!style)
return false;
StyleItem* item = new (nothrow) StyleItem;
if (!item)
return false;
item->style = style;
item->transformation = transformation;
// TODO: if the style uses a gradient and the gradient
// should be transformed along with the shape, then
// we should multiply the item->transformation with the
// gradient transformation
item->transformation.invert();
return fStyles.AddItem((void*)item);
}
private:
@ -59,7 +83,7 @@ private:
void _GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
GradientFunction function,
const agg::rgba8* gradientColors,
agg::trans_affine& gradientTransform);
Transformation& gradientTransform);
BList fStyles;
GammaTable& fGammaTable;
@ -71,13 +95,13 @@ private:
const agg::rgba8&
StyleHandler::color(unsigned styleIndex)
{
Style* style = (Style*)fStyles.ItemAt(styleIndex);
if (!style) {
StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
if (!styleItem) {
printf("no style at index: %d!\n", styleIndex);
return fTransparent;
}
const rgb_color& c = style->Color();
const rgb_color& c = styleItem->style->Color();
fColor = agg::rgba8(fGammaTable.dir(c.red),
fGammaTable.dir(c.green),
fGammaTable.dir(c.blue),
@ -91,55 +115,52 @@ void
StyleHandler::generate_span(agg::rgba8* span, int x, int y,
unsigned len, unsigned styleIndex)
{
Style* style = (Style*)fStyles.ItemAt(styleIndex);
if (!style || !style->Gradient()) {
StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
if (!styleItem || !styleItem->style->Gradient()) {
printf("no style/gradient at index: %d!\n", styleIndex);
// TODO: memset() span?
return;
}
Style* style = styleItem->style;
Gradient* gradient = style->Gradient();
const agg::rgba8* colors = style->Colors();
agg::trans_affine transformation;
// TODO: construct the gradient transformation here
// remember to invert() it!
switch (gradient->Type()) {
case GRADIENT_LINEAR: {
agg::gradient_x function;
_GenerateGradient(span, x, y, len, function, colors,
transformation);
styleItem->transformation);
break;
}
case GRADIENT_CIRCULAR: {
agg::gradient_radial function;
_GenerateGradient(span, x, y, len, function, colors,
transformation);
styleItem->transformation);
break;
}
case GRADIENT_DIAMONT: {
agg::gradient_diamond function;
_GenerateGradient(span, x, y, len, function, colors,
transformation);
styleItem->transformation);
break;
}
case GRADIENT_CONIC: {
agg::gradient_conic function;
_GenerateGradient(span, x, y, len, function, colors,
transformation);
styleItem->transformation);
break;
}
case GRADIENT_XY: {
agg::gradient_xy function;
_GenerateGradient(span, x, y, len, function, colors,
transformation);
styleItem->transformation);
break;
}
case GRADIENT_SQRT_XY: {
agg::gradient_sqrt_xy function;
_GenerateGradient(span, x, y, len, function, colors,
transformation);
styleItem->transformation);
break;
}
}
@ -151,7 +172,7 @@ void
StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
GradientFunction function,
const agg::rgba8* gradientColors,
agg::trans_affine& gradientTransform)
Transformation& gradientTransform)
{
typedef agg::pod_auto_array<agg::rgba8, 256> ColorArray;
@ -262,6 +283,7 @@ IconRenderer::_Render(const BRect& r)
fBaseRendererPre.clear(agg::rgba8(0, 0, 0, 0));
//bigtime_t start = system_time();
StyleHandler styleHandler(fGammaTable);
fRasterizer.reset();
@ -272,19 +294,21 @@ IconRenderer::_Render(const BRect& r)
for (int32 i = 0; i < shapeCount; i++) {
Shape* shape = fIcon->Shapes()->ShapeAtFast(i);
if (!styleHandler.AddStyle(shape->Style())) {
Transformation transform(*shape);
transform.multiply(fGlobalTransform);
// NOTE: this works only because "agg::trans_affine",
// "Transformable" and "Transformation" are all the
// same thing
if (!styleHandler.AddStyle(shape->Style(), transform)) {
printf("IconRenderer::_Render() - out of memory\n");
break;
}
fRasterizer.styles(i, -1);
if (fGlobalTransform.is_identity()) {
fRasterizer.add_path(shape->VertexSource());
} else {
agg::conv_transform<VertexSource, Transformation>
scaledPath(shape->VertexSource(), fGlobalTransform);
fRasterizer.add_path(scaledPath);
}
agg::conv_transform<VertexSource, Transformation>
scaledPath(shape->VertexSource(), transform);
fRasterizer.add_path(scaledPath);
}
agg::render_scanlines_compound(fRasterizer,
@ -296,5 +320,8 @@ IconRenderer::_Render(const BRect& r)
if (fGammaTable.gamma() != 1.0)
fPixelFormat.apply_gamma_inv(fGammaTable);
//if (fRenderingBuffer.width() == 64)
//printf("rendering 64x64: %lld\n", system_time() - start);
}

View File

@ -25,6 +25,7 @@ ShapeListener::~ShapeListener()
// constructor
Shape::Shape(::Style* style)
: IconObject("<shape>"),
Transformable(),
Observer(),
PathContainerListener(),
@ -46,6 +47,7 @@ Shape::Shape(::Style* style)
// constructor
Shape::Shape(const Shape& other)
: IconObject(other),
Transformable(other),
Observer(),
PathContainerListener(),
@ -94,6 +96,16 @@ Shape::~Shape()
// #pragma mark -
// TransformationChanged
void
Shape::TransformationChanged()
{
// TODO: notify appearance change
_NotifyRerender();
}
// #pragma mark -
// ObjectChanged
void
Shape::ObjectChanged(const Observable* object)
@ -219,7 +231,9 @@ Shape::Bounds(bool updateLast) const
double left, top, right, bottom;
::VertexSource& source = const_cast<Shape*>(this)->VertexSource();
agg::bounding_rect(source, pathID, 0, 1,
agg::conv_transform< ::VertexSource, Transformable>
transformedSource(source, *this);
agg::bounding_rect(transformedSource, pathID, 0, 1,
&left, &top, &right, &bottom);
BRect bounds(left, top, right, bottom);

View File

@ -8,6 +8,7 @@
#include "Observer.h"
#include "PathContainer.h"
#include "PathSource.h"
#include "Transformable.h"
#include "VectorPath.h"
class Style;
@ -28,6 +29,7 @@ class ShapeListener {
};
class Shape : public IconObject,
public Transformable,
public Observer, // observing all the paths and the style
public PathContainerListener,
public PathListener {
@ -36,6 +38,9 @@ class Shape : public IconObject,
Shape(const Shape& other);
virtual ~Shape();
// Transformable interface
virtual void TransformationChanged();
// Observer interface
virtual void ObjectChanged(const Observable* object);

View File

@ -0,0 +1,227 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "ChannelTransform.h"
#include <stdio.h>
// constructor
ChannelTransform::ChannelTransform()
: Transformable(),
fPivot(0.0, 0.0),
fTranslation(0.0, 0.0),
fRotation(0.0),
fXScale(1.0),
fYScale(1.0)
{
}
// copy constructor
ChannelTransform::ChannelTransform(const ChannelTransform& other)
: Transformable(other),
fPivot(other.fPivot),
fTranslation(other.fTranslation),
fRotation(other.fRotation),
fXScale(other.fXScale),
fYScale(other.fYScale)
{
}
// destructor
ChannelTransform::~ChannelTransform()
{
}
// SetTransformation
void
ChannelTransform::SetTransformation(BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale)
{
if (fTranslation != translation ||
fPivot != pivot ||
fRotation != rotation ||
fXScale != xScale ||
fYScale != yScale) {
fPivot = pivot;
fTranslation = translation;
fRotation = rotation;
fXScale = xScale;
fYScale = yScale;
_UpdateMatrix();
}
}
// SetPivot
void
ChannelTransform::SetPivot(BPoint pivot)
{
if (pivot == fPivot)
return;
fPivot = pivot;
_UpdateMatrix();
}
// TranslateBy
void
ChannelTransform::TranslateBy(BPoint offset)
{
if (offset.x == 0.0 && offset.y == 0.0)
return;
fTranslation += offset;
_UpdateMatrix();
}
// RotateBy
//
// converts a rotation in world coordinates into
// a combined local rotation and a translation
void
ChannelTransform::RotateBy(BPoint origin, double degrees)
{
if (degrees == 0.0)
return;
origin -= fPivot;
fRotation += degrees;
// rotate fTranslation
double xOffset = fTranslation.x - origin.x;
double yOffset = fTranslation.y - origin.y;
agg::trans_affine_rotation m(degrees * PI / 180.0);
m.transform(&xOffset, &yOffset);
fTranslation.x = origin.x + xOffset;
fTranslation.y = origin.y + yOffset;
_UpdateMatrix();
}
// RotateBy
void
ChannelTransform::RotateBy(double degrees)
{
if (degrees == 0.0)
return;
fRotation += degrees;
_UpdateMatrix();
}
//// ScaleBy
////
//// converts a scalation in world coordinates into
//// a combined local scalation and a translation
//void
//ChannelTransform::ScaleBy(BPoint origin, double xScale, double yScale)
//{
// if (xScale == 1.0 && yScale == 1.0)
// return;
//
// fXScale *= xScale;
// fYScale *= yScale;
//
// // scale fTranslation
// double xOffset = fTranslation.x - origin.x;
// double yOffset = fTranslation.y - origin.y;
//
// fTranslation.x = origin.x + (xOffset * xScale);
// fTranslation.y = origin.y + (yOffset * yScale);
//
// _UpdateMatrix();
//}
// ScaleBy
void
ChannelTransform::ScaleBy(double xScale, double yScale)
{
if (xScale == 1.0 && yScale == 1.0)
return;
fXScale *= xScale;
fYScale *= yScale;
_UpdateMatrix();
}
// SetTranslationAndScale
void
ChannelTransform::SetTranslationAndScale(BPoint offset,
double xScale, double yScale)
{
if (fTranslation == offset && fXScale == xScale && fYScale == yScale)
return;
fTranslation = offset;
fXScale = xScale;
fYScale = yScale;
_UpdateMatrix();
}
// Reset
void
ChannelTransform::Reset()
{
SetTransformation(B_ORIGIN, B_ORIGIN, 0.0, 1.0, 1.0);
}
// =
ChannelTransform&
ChannelTransform::operator=(const ChannelTransform& other)
{
fTranslation = other.fTranslation;
fRotation = other.fRotation;
fXScale = other.fXScale;
fYScale = other.fYScale;
Transformable::operator=(other);
return *this;
}
// _UpdateMatrix
void
ChannelTransform::_UpdateMatrix()
{
// fix up scales in case any is zero
double xScale = fXScale;
if (xScale == 0.0)
xScale = 0.000001;
double yScale = fYScale;
if (yScale == 0.0)
yScale = 0.000001;
// start clean
reset();
// the "pivot" is like the offset from world to local
// coordinate system and is the center for rotation and scale
multiply(agg::trans_affine_translation(-fPivot.x, -fPivot.y));
multiply(agg::trans_affine_scaling(xScale, yScale));
multiply(agg::trans_affine_rotation(fRotation * PI / 180.0));
multiply(agg::trans_affine_translation(fPivot.x + fTranslation.x,
fPivot.y + fTranslation.y));
// call hook function
Update();
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef CHANNEL_TRANSFORM_H
#define CHANNEL_TRANSFORM_H
#include "Transformable.h"
class ChannelTransform : public Transformable {
public:
ChannelTransform();
ChannelTransform(const ChannelTransform& other);
virtual ~ChannelTransform();
// ChannelTransform
virtual void Update(bool deep = true) {}
void SetTransformation(BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale);
void SetPivot(BPoint pivot);
virtual void TranslateBy(BPoint offset);
virtual void RotateBy(BPoint origin, double degrees);
void ScaleBy(double xScale, double yScale);
void RotateBy(double degrees);
void SetTranslationAndScale(BPoint offset,
double xScale,
double yScale);
virtual void Reset();
inline BPoint Pivot() const
{ return fPivot; }
inline BPoint Translation() const
{ return fTranslation; }
inline double LocalRotation() const
{ return fRotation; }
inline double LocalXScale() const
{ return fXScale; }
inline double LocalYScale() const
{ return fYScale; }
ChannelTransform& operator=(const ChannelTransform& other);
private:
void _UpdateMatrix();
BPoint fPivot;
BPoint fTranslation;
double fRotation;
double fXScale;
double fYScale;
};
#endif // CHANNEL_TRANSFORM_H

View File

@ -0,0 +1,594 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "TransformBox.h"
#include <stdio.h>
#include <agg_trans_affine.h>
#include <agg_math.h>
#include <View.h>
#include "support.h"
#include "TransformBoxStates.h"
#include "StateView.h"
#include "TransformCommand.h"
#define INSET 8.0
// constructor
TransformBox::TransformBox(StateView* view, BRect box)
: ChannelTransform(),
Manipulator(NULL),
fOriginalBox(box),
fLeftTop(box.LeftTop()),
fRightTop(box.RightTop()),
fLeftBottom(box.LeftBottom()),
fRightBottom(box.RightBottom()),
fPivot((fLeftTop.x + fRightBottom.x) / 2.0,
(fLeftTop.y + fRightBottom.y) / 2.0),
fPivotOffset(B_ORIGIN),
fCurrentCommand(NULL),
fCurrentState(NULL),
fDragging(false),
fMousePos(-10000.0, -10000.0),
fModifiers(0),
fNudging(false),
fView(view),
fDragLTState(new DragCornerState(this, DragCornerState::LEFT_TOP_CORNER)),
fDragRTState(new DragCornerState(this, DragCornerState::RIGHT_TOP_CORNER)),
fDragLBState(new DragCornerState(this, DragCornerState::LEFT_BOTTOM_CORNER)),
fDragRBState(new DragCornerState(this, DragCornerState::RIGHT_BOTTOM_CORNER)),
fDragLState(new DragSideState(this, DragSideState::LEFT_SIDE)),
fDragRState(new DragSideState(this, DragSideState::RIGHT_SIDE)),
fDragTState(new DragSideState(this, DragSideState::TOP_SIDE)),
fDragBState(new DragSideState(this, DragSideState::BOTTOM_SIDE)),
fRotateState(new RotateBoxState(this)),
fTranslateState(new DragBoxState(this)),
fOffsetCenterState(new OffsetCenterState(this))
{
}
// destructor
TransformBox::~TransformBox()
{
delete fCurrentCommand;
delete fDragLTState;
delete fDragRTState;
delete fDragLBState;
delete fDragRBState;
delete fDragLState;
delete fDragRState;
delete fDragTState;
delete fDragBState;
delete fRotateState;
delete fTranslateState;
delete fOffsetCenterState;
}
// Draw
void
TransformBox::Draw(BView* into, BRect updateRect)
{
// convert to canvas view coordinates
BPoint lt = fLeftTop;
BPoint rt = fRightTop;
BPoint lb = fLeftBottom;
BPoint rb = fRightBottom;
BPoint c = fPivot;
TransformFromCanvas(lt);
TransformFromCanvas(rt);
TransformFromCanvas(lb);
TransformFromCanvas(rb);
TransformFromCanvas(c);
into->SetDrawingMode(B_OP_COPY);
into->SetHighColor(255, 255, 255, 255);
into->SetLowColor(0, 0, 0, 255);
_StrokeBWLine(into, lt, rt);
_StrokeBWLine(into, rt, rb);
_StrokeBWLine(into, rb, lb);
_StrokeBWLine(into, lb, lt);
double rotation = ViewSpaceRotation();
_StrokeBWPoint(into, lt, rotation);
_StrokeBWPoint(into, rt, rotation + 90.0);
_StrokeBWPoint(into, rb, rotation + 180.0);
_StrokeBWPoint(into, lb, rotation + 270.0);
BRect cr(c, c);
cr.InsetBy(-3.0, -3.0);
into->StrokeEllipse(cr, B_SOLID_HIGH);
cr.InsetBy(1.0, 1.0);
into->StrokeEllipse(cr, B_SOLID_LOW);
into->SetDrawingMode(B_OP_COPY);
}
// #pragma mark -
// MouseDown
bool
TransformBox::MouseDown(BPoint where)
{
TransformToCanvas(where);
fDragging = true;
if (fCurrentState) {
fCurrentState->SetOrigin(where);
delete fCurrentCommand;
fCurrentCommand = MakeAction(fCurrentState->ActionName(),
fCurrentState->ActionNameIndex());
}
return true;
}
// MouseMoved
void
TransformBox::MouseMoved(BPoint where)
{
TransformToCanvas(where);
if (fMousePos != where) {
fMousePos = where;
if (fCurrentState) {
fCurrentState->DragTo(fMousePos, fModifiers);
fCurrentState->UpdateViewCursor(fView, fMousePos);
}
}
}
// MouseUp
Command*
TransformBox::MouseUp()
{
fDragging = false;
return FinishTransaction();
}
// MouseOver
bool
TransformBox::MouseOver(BPoint where)
{
TransformToCanvas(where);
_SetState(_DragStateFor(where, 1.0 /*zoom*/));
fMousePos = where;
if (fCurrentState) {
fCurrentState->UpdateViewCursor(fView, fMousePos);
return true;
}
return false;
}
// DoubleClicked
bool
TransformBox::DoubleClicked(BPoint where)
{
return false;
}
// #pragma mark -
// Bounds
BRect
TransformBox::Bounds()
{
// convert from canvas view coordinates
BPoint lt = fLeftTop;
BPoint rt = fRightTop;
BPoint lb = fLeftBottom;
BPoint rb = fRightBottom;
BPoint c = Pivot();
TransformFromCanvas(lt);
TransformFromCanvas(rt);
TransformFromCanvas(lb);
TransformFromCanvas(rb);
TransformFromCanvas(c);
BRect r;
r.left = min5(lt.x, rt.x, lb.x, rb.x, c.x);
r.top = min5(lt.y, rt.y, lb.y, rb.y, c.y);
r.right = max5(lt.x, rt.x, lb.x, rb.x, c.x);
r.bottom = max5(lt.y, rt.y, lb.y, rb.y, c.y);
return r;
}
// TrackingBounds
BRect
TransformBox::TrackingBounds(BView* withinView)
{
return withinView->Bounds();
}
// #pragma mark -
// ModifiersChanged
void
TransformBox::ModifiersChanged(uint32 modifiers)
{
fModifiers = modifiers;
if (fDragging && fCurrentState) {
fCurrentState->DragTo(fMousePos, fModifiers);
}
}
// HandleKeyDown
bool
TransformBox::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
{
// TODO: nudging
return false;
}
// HandleKeyUp
bool
TransformBox::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
{
// TODO: nudging
return false;
}
// #pragma mark -
// UpdateCursor
void
TransformBox::UpdateCursor()
{
if (fCurrentState)
fCurrentState->UpdateViewCursor(fView, fMousePos);
}
// AttachedToView
void
TransformBox::AttachedToView(BView* view)
{
view->Invalidate(Bounds().InsetByCopy(-INSET, -INSET));
}
// DetachedFromView
void
TransformBox::DetachedFromView(BView* view)
{
view->Invalidate(Bounds().InsetByCopy(-INSET, -INSET));
}
// pragma mark -
// Update
void
TransformBox::Update(bool deep)
{
// recalculate the points from the original box
fLeftTop = fOriginalBox.LeftTop();
fRightTop = fOriginalBox.RightTop();
fLeftBottom = fOriginalBox.LeftBottom();
fRightBottom = fOriginalBox.RightBottom();
fPivot.x = (fLeftTop.x + fRightBottom.x) / 2.0;
fPivot.y = (fLeftTop.y + fRightBottom.y) / 2.0;
fPivot += fPivotOffset;
// transform the points for display
Transform(&fLeftTop);
Transform(&fRightTop);
Transform(&fLeftBottom);
Transform(&fRightBottom);
Transform(&fPivot);
}
// OffsetPivot
void
TransformBox::OffsetPivot(BPoint offset)
{
if (offset != BPoint(0.0, 0.0)) {
fPivotOffset += offset;
Update(false);
}
}
// SetBox
void
TransformBox::SetBox(BRect box)
{
if (fOriginalBox != box) {
fOriginalBox = box;
Update(false);
}
}
// FinishTransaction
Command*
TransformBox::FinishTransaction()
{
Command* command = fCurrentCommand;
if (fCurrentCommand) {
fCurrentCommand->SetNewTransformation(Pivot(),
Translation(),
LocalRotation(),
LocalXScale(),
LocalYScale());
fCurrentCommand = NULL;
}
return command;
}
// NudgeBy
void
TransformBox::NudgeBy(BPoint offset)
{
if (!fNudging && !fCurrentCommand) {
fCurrentCommand = MakeAction("Move", 0/*MOVE*/);
fNudging = true;
}
if (fNudging) {
TranslateBy(offset);
}
}
// FinishNudging
Command*
TransformBox::FinishNudging()
{
fNudging = false;
return FinishTransaction();
}
// TransformFromCanvas
void
TransformBox::TransformFromCanvas(BPoint& point) const
{
}
// TransformToCanvas
void
TransformBox::TransformToCanvas(BPoint& point) const
{
}
// ViewSpaceRotation
double
TransformBox::ViewSpaceRotation() const
{
// assume no inherited transformation
return LocalRotation();
}
// TODO: why another version?
// point_line_dist
float
point_line_dist(BPoint start, BPoint end, BPoint p, float radius)
{
BRect r(min_c(start.x, end.x),
min_c(start.y, end.y),
max_c(start.x, end.x),
max_c(start.y, end.y));
r.InsetBy(-radius, -radius);
if (r.Contains(p)) {
return fabs(agg::calc_line_point_distance(start.x, start.y,
end.x, end.y,
p.x, p.y));
}
return min_c(point_point_distance(start, p),
point_point_distance(end, p));
}
// _DragStateFor
//
// where is expected in canvas view coordinates
DragState*
TransformBox::_DragStateFor(BPoint where, float canvasZoom)
{
DragState* state = NULL;
// convert to canvas zoom level
//
// the conversion is necessary, because the "hot regions"
// around a point should be the same size no matter what
// zoom level the canvas is displayed at
float inset = INSET / canvasZoom;
// priorities:
// transformation center point has highest priority ?!?
if (point_point_distance(where, Pivot()) < inset)
state = fOffsetCenterState;
if (!state) {
// next, the inner area of the box
// for the following calculations
// we can apply the inverse transformation to all points
// this way we have to consider BRects only, not transformed polygons
BPoint lt = fLeftTop;
BPoint rb = fRightBottom;
BPoint w = where;
InverseTransform(&w);
InverseTransform(&lt);
InverseTransform(&rb);
// next priority has the inside of the box
BRect iR(lt, rb);
float hInset = min_c(inset, max_c(0, (iR.Width() - inset) / 2.0));
float vInset = min_c(inset, max_c(0, (iR.Height() - inset) / 2.0));
iR.InsetBy(hInset, vInset);
if (iR.Contains(w))
state = fTranslateState;
}
if (!state) {
// next priority have the corners
float dLT = point_point_distance(fLeftTop, where);
float dRT = point_point_distance(fRightTop, where);
float dLB = point_point_distance(fLeftBottom, where);
float dRB = point_point_distance(fRightBottom, where);
float d = min4(dLT, dRT, dLB, dRB);
if (d < inset) {
if (d == dLT)
state = fDragLTState;
else if (d == dRT)
state = fDragRTState;
else if (d == dLB)
state = fDragLBState;
else if (d == dRB)
state = fDragRBState;
}
}
if (!state) {
// next priority have the sides
float dL = point_line_dist(fLeftTop, fLeftBottom, where, inset);
float dR = point_line_dist(fRightTop, fRightBottom, where, inset);
float dT = point_line_dist(fLeftTop, fRightTop, where, inset);
float dB = point_line_dist(fLeftBottom, fRightBottom, where, inset);
float d = min4(dL, dR, dT, dB);
if (d < inset) {
if (d == dL)
state = fDragLState;
else if (d == dR)
state = fDragRState;
else if (d == dT)
state = fDragTState;
else if (d == dB)
state = fDragBState;
}
}
if (!state) {
BPoint lt = fLeftTop;
BPoint rb = fRightBottom;
BPoint w = where;
InverseTransform(&w);
InverseTransform(&lt);
InverseTransform(&rb);
// check inside of the box again
BRect iR(lt, rb);
if (iR.Contains(w)) {
state = fTranslateState;
} else {
// last priority has the rotate state
state = fRotateState;
}
}
return state;
}
// _StrokeBWLine
void
TransformBox::_StrokeBWLine(BView* into, BPoint from, BPoint to) const
{
// find out how to offset the second line optimally
BPoint offset(0.0, 0.0);
// first, do we have a more horizontal line or a more vertical line?
float xDiff = to.x - from.x;
float yDiff = to.y - from.y;
if (fabs(xDiff) > fabs(yDiff)) {
// horizontal
if (xDiff > 0.0) {
offset.y = -1.0;
} else {
offset.y = 1.0;
}
} else {
// vertical
if (yDiff < 0.0) {
offset.x = -1.0;
} else {
offset.x = 1.0;
}
}
// stroke two lines in high and low color of the view
into->StrokeLine(from, to, B_SOLID_LOW);
from += offset;
to += offset;
into->StrokeLine(from, to, B_SOLID_HIGH);
}
// _StrokeBWPoint
void
TransformBox::_StrokeBWPoint(BView* into, BPoint point, double angle) const
{
double x = point.x;
double y = point.y;
double x1 = x;
double y1 = y - 5.0;
double x2 = x - 5.0;
double y2 = y - 5.0;
double x3 = x - 5.0;
double y3 = y;
agg::trans_affine m;
double xOffset = -x;
double yOffset = -y;
agg::trans_affine_rotation r(angle * PI / 180.0);
r.transform(&xOffset, &yOffset);
xOffset = x + xOffset;
yOffset = y + yOffset;
m.multiply(r);
m.multiply(agg::trans_affine_translation(xOffset, yOffset));
m.transform(&x, &y);
m.transform(&x1, &y1);
m.transform(&x2, &y2);
m.transform(&x3, &y3);
BPoint p[4];
p[0] = BPoint(x, y);
p[1] = BPoint(x1, y1);
p[2] = BPoint(x2, y2);
p[3] = BPoint(x3, y3);
into->FillPolygon(p, 4, B_SOLID_HIGH);
into->StrokeLine(p[0], p[1], B_SOLID_LOW);
into->StrokeLine(p[1], p[2], B_SOLID_LOW);
into->StrokeLine(p[2], p[3], B_SOLID_LOW);
into->StrokeLine(p[3], p[0], B_SOLID_LOW);
}
// _SetState
void
TransformBox::_SetState(DragState* state)
{
if (state != fCurrentState) {
fCurrentState = state;
fCurrentState->UpdateViewCursor(fView, fMousePos);
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef TRANSFORM_BOX_H
#define TRANSFORM_BOX_H
#include "ChannelTransform.h"
#include "Manipulator.h"
class Command;
class StateView;
class DragState;
class TransformCommand;
class TransformBox : public ChannelTransform,
public Manipulator {
public:
TransformBox(StateView* view,
BRect box);
virtual ~TransformBox();
// Manipulator interface
virtual void Draw(BView* into, BRect updateRect);
virtual bool MouseDown(BPoint where);
virtual void MouseMoved(BPoint where);
virtual Command* MouseUp();
virtual bool MouseOver(BPoint where);
virtual bool DoubleClicked(BPoint where);
virtual BRect Bounds();
virtual BRect TrackingBounds(BView* withinView);
virtual void ModifiersChanged(uint32 modifiers);
virtual bool HandleKeyDown(uint32 key, uint32 modifiers,
Command** _command);
virtual bool HandleKeyUp(uint32 key, uint32 modifiers,
Command** _command);
virtual void UpdateCursor();
virtual void AttachedToView(BView* view);
virtual void DetachedFromView(BView* view);
// TransformBox
virtual void Update(bool deep = true);
void OffsetPivot(BPoint offset);
void SetBox(BRect box);
BRect Box() const
{ return fOriginalBox; }
Command* FinishTransaction();
void NudgeBy(BPoint offset);
bool IsNudging() const
{ return fNudging; }
Command* FinishNudging();
virtual void TransformFromCanvas(BPoint& point) const;
virtual void TransformToCanvas(BPoint& point) const;
virtual TransformCommand* MakeAction(const char* actionName,
uint32 nameIndex) const = 0;
bool IsRotating() const
{ return fCurrentState == fRotateState; }
virtual double ViewSpaceRotation() const;
private:
DragState* _DragStateFor(BPoint canvasWhere,
float canvasZoom);
void _StrokeBWLine(BView* into,
BPoint from, BPoint to) const;
void _StrokeBWPoint(BView* into,
BPoint point, double angle) const;
BRect fOriginalBox;
BPoint fLeftTop;
BPoint fRightTop;
BPoint fLeftBottom;
BPoint fRightBottom;
BPoint fPivot;
BPoint fPivotOffset;
TransformCommand* fCurrentCommand;
DragState* fCurrentState;
bool fDragging;
BPoint fMousePos;
uint32 fModifiers;
bool fNudging;
protected:
// "static" state objects
void _SetState(DragState* state);
StateView* fView;
DragState* fDragLTState;
DragState* fDragRTState;
DragState* fDragLBState;
DragState* fDragRBState;
DragState* fDragLState;
DragState* fDragRState;
DragState* fDragTState;
DragState* fDragBState;
DragState* fRotateState;
DragState* fTranslateState;
DragState* fOffsetCenterState;
};
#endif // TRANSFORM_BOX_H

View File

@ -0,0 +1,615 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "TransformBoxStates.h"
#include <math.h>
#include <stdio.h>
#include <Cursor.h>
#include <InterfaceDefs.h>
#include <View.h>
#include "cursors.h"
#include "support.h"
#include "TransformBox.h"
//#include "Strings.h"
// constructor
DragState::DragState(TransformBox* parent)
: fOrigin(0.0, 0.0),
fParent(parent)
{
}
// SetOrigin
void
DragState::SetOrigin(BPoint origin)
{
fOrigin = origin;
}
// ActionName
const char*
DragState::ActionName() const
{
return "Transformation";
}
// ActionNameIndex
uint32
DragState::ActionNameIndex() const
{
return TRANSFORMATION;
}
// _SetViewCursor
void
DragState::_SetViewCursor(BView* view, const uchar* cursorData) const
{
BCursor cursor(cursorData);
view->SetViewCursor(&cursor);
}
// #pragma mark - DragCornerState
// constructor
DragCornerState::DragCornerState(TransformBox* parent, uint32 corner)
: DragState(parent),
fCorner(corner)
{
}
// SetOrigin
void
DragCornerState::SetOrigin(BPoint origin)
{
fOldXScale = fParent->LocalXScale();
fOldYScale = fParent->LocalYScale();
fOldOffset = fParent->Translation();
// copy the matrix at the start of the drag procedure
fMatrix.reset();
fMatrix.multiply(agg::trans_affine_scaling(fOldXScale, fOldYScale));
fMatrix.multiply(agg::trans_affine_rotation(fParent->LocalRotation() * PI / 180.0));
fMatrix.multiply(agg::trans_affine_translation(fParent->Translation().x,
fParent->Translation().y));
double x = origin.x;
double y = origin.y;
fMatrix.inverse_transform(&x, &y);
origin.x = x;
origin.y = y;
BRect box = fParent->Box();
switch (fCorner) {
case LEFT_TOP_CORNER:
fXOffsetFromCorner = origin.x - box.left;
fYOffsetFromCorner = origin.y - box.top;
fOldWidth = box.left - box.right;
fOldHeight = box.top - box.bottom;
origin.x = box.right;
origin.y = box.bottom;
break;
case RIGHT_TOP_CORNER:
fXOffsetFromCorner = origin.x - box.right;
fYOffsetFromCorner = origin.y - box.top;
fOldWidth = box.right - box.left;
fOldHeight = box.top - box.bottom;
origin.x = box.left;
origin.y = box.bottom;
break;
case LEFT_BOTTOM_CORNER:
fXOffsetFromCorner = origin.x - box.left;
fYOffsetFromCorner = origin.y - box.bottom;
fOldWidth = box.left - box.right;
fOldHeight = box.bottom - box.top;
origin.x = box.right;
origin.y = box.top;
break;
case RIGHT_BOTTOM_CORNER:
fXOffsetFromCorner = origin.x - box.right;
fYOffsetFromCorner = origin.y - box.bottom;
fOldWidth = box.right - box.left;
fOldHeight = box.bottom - box.top;
origin.x = box.left;
origin.y = box.top;
break;
}
DragState::SetOrigin(origin);
}
// DragTo
void
DragCornerState::DragTo(BPoint current, uint32 modifiers)
{
double x = current.x;
double y = current.y;
fMatrix.inverse_transform(&x, &y);
double xScale = 1.0;
double yScale = 1.0;
BPoint translation(0.0, 0.0);
switch (fCorner) {
case LEFT_TOP_CORNER:
case RIGHT_TOP_CORNER:
case LEFT_BOTTOM_CORNER:
case RIGHT_BOTTOM_CORNER:
x -= fOrigin.x;
y -= fOrigin.y;
if (fOldWidth != 0.0)
xScale = (x - fXOffsetFromCorner) / (fOldWidth);
if (fOldHeight != 0.0)
yScale = (y - fYOffsetFromCorner) / (fOldHeight);
// constrain aspect ratio if shift is pressed
if (modifiers & B_SHIFT_KEY) {
if (fabs(xScale) > fabs(yScale))
yScale = yScale > 0.0 ? fabs(xScale) : -fabs(xScale);
else
xScale = xScale > 0.0 ? fabs(yScale) : -fabs(yScale);
}
translation.x = fOrigin.x - fOrigin.x * xScale;
translation.y = fOrigin.y - fOrigin.y * yScale;
break;
}
x = translation.x;
y = translation.y;
fMatrix.transform(&x, &y);
translation.x = x;
translation.y = y;
fParent->SetTranslationAndScale(translation,
xScale * fOldXScale,
yScale * fOldYScale);
}
// UpdateViewCursor
void
DragCornerState::UpdateViewCursor(BView* view, BPoint current) const
{
float rotation = fmod(360.0 - fParent->ViewSpaceRotation() + 22.5, 180.0);
bool flipX = fParent->LocalXScale() < 0.0;
bool flipY = fParent->LocalYScale() < 0.0;
if (rotation < 45.0) {
switch (fCorner) {
case LEFT_TOP_CORNER:
case RIGHT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kLeftTopRightBottomCursor
: kLeftBottomRightTopCursor);
else
_SetViewCursor(view, flipY ? kLeftBottomRightTopCursor
: kLeftTopRightBottomCursor);
break;
case RIGHT_TOP_CORNER:
case LEFT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kLeftBottomRightTopCursor
: kLeftTopRightBottomCursor);
else
_SetViewCursor(view, flipY ? kLeftTopRightBottomCursor
: kLeftBottomRightTopCursor);
break;
}
} else if (rotation < 90.0) {
switch (fCorner) {
case LEFT_TOP_CORNER:
case RIGHT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kLeftRightCursor
: kUpDownCursor);
else
_SetViewCursor(view, flipY ? kUpDownCursor
: kLeftRightCursor);
break;
case RIGHT_TOP_CORNER:
case LEFT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kUpDownCursor
: kLeftRightCursor);
else
_SetViewCursor(view, flipY ? kLeftRightCursor
: kUpDownCursor);
break;
}
} else if (rotation < 135.0) {
switch (fCorner) {
case LEFT_TOP_CORNER:
case RIGHT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kLeftBottomRightTopCursor
: kLeftTopRightBottomCursor);
else
_SetViewCursor(view, flipY ? kLeftTopRightBottomCursor
: kLeftBottomRightTopCursor);
break;
break;
case RIGHT_TOP_CORNER:
case LEFT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kLeftTopRightBottomCursor
: kLeftBottomRightTopCursor);
else
_SetViewCursor(view, flipY ? kLeftBottomRightTopCursor
: kLeftTopRightBottomCursor);
break;
break;
}
} else {
switch (fCorner) {
case LEFT_TOP_CORNER:
case RIGHT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kUpDownCursor
: kLeftRightCursor);
else
_SetViewCursor(view, flipY ? kLeftRightCursor
: kUpDownCursor);
break;
case RIGHT_TOP_CORNER:
case LEFT_BOTTOM_CORNER:
if (flipX)
_SetViewCursor(view, flipY ? kLeftRightCursor
: kUpDownCursor);
else
_SetViewCursor(view, flipY ? kUpDownCursor
: kLeftRightCursor);
break;
}
}
}
// ActionName
const char*
DragCornerState::ActionName() const
{
return "Scale";
}
// ActionNameIndex
uint32
DragCornerState::ActionNameIndex() const
{
return SCALE;
}
// #pragma mark - DragSideState
DragSideState::DragSideState(TransformBox* parent, uint32 side)
: DragState(parent),
fSide(side)
{
}
// SetOrigin
void
DragSideState::SetOrigin(BPoint origin)
{
fOldXScale = fParent->LocalXScale();
fOldYScale = fParent->LocalYScale();
fOldOffset = fParent->Translation();
// copy the matrix at the start of the drag procedure
fMatrix.reset();
fMatrix.multiply(agg::trans_affine_scaling(fOldXScale, fOldYScale));
fMatrix.multiply(agg::trans_affine_rotation(fParent->LocalRotation() * PI / 180.0));
fMatrix.multiply(agg::trans_affine_translation(fParent->Translation().x,
fParent->Translation().y));
double x = origin.x;
double y = origin.y;
fMatrix.inverse_transform(&x, &y);
origin.x = x;
origin.y = y;
BRect box = fParent->Box();
switch (fSide) {
case LEFT_SIDE:
fOffsetFromSide = origin.x - box.left;
fOldSideDist = box.left - box.right;
origin.x = box.right;
break;
case RIGHT_SIDE:
fOffsetFromSide = origin.x - box.right;
fOldSideDist = box.right - box.left;
origin.x = box.left;
break;
case TOP_SIDE:
fOffsetFromSide = origin.y - box.top;
fOldSideDist = box.top - box.bottom;
origin.y = box.bottom;
break;
case BOTTOM_SIDE:
fOffsetFromSide = origin.y - box.bottom;
fOldSideDist = box.bottom - box.top;
origin.y = box.top;
break;
}
DragState::SetOrigin(origin);
}
// DragTo
void
DragSideState::DragTo(BPoint current, uint32 modifiers)
{
double x = current.x;
double y = current.y;
fMatrix.inverse_transform(&x, &y);
double xScale = 1.0;
double yScale = 1.0;
BPoint translation(0.0, 0.0);
switch (fSide) {
case LEFT_SIDE:
case RIGHT_SIDE:
x -= fOrigin.x;
if (fOldSideDist != 0.0)
xScale = (x - fOffsetFromSide) / (fOldSideDist);
translation.x = fOrigin.x - fOrigin.x * xScale;
break;
case TOP_SIDE:
case BOTTOM_SIDE:
y -= fOrigin.y;
if (fOldSideDist != 0.0)
yScale = (y - fOffsetFromSide) / (fOldSideDist);
translation.y = fOrigin.y - fOrigin.y * yScale;
break;
}
x = translation.x;
y = translation.y;
fMatrix.transform(&x, &y);
translation.x = x;
translation.y = y;
fParent->SetTranslationAndScale(translation,
xScale * fOldXScale,
yScale * fOldYScale);
}
// UpdateViewCursor
void
DragSideState::UpdateViewCursor(BView* view, BPoint current) const
{
float rotation = fmod(360.0 - fParent->ViewSpaceRotation() + 22.5, 180.0);
if (rotation < 45.0) {
switch (fSide) {
case LEFT_SIDE:
case RIGHT_SIDE:
_SetViewCursor(view, kLeftRightCursor);
break;
case TOP_SIDE:
case BOTTOM_SIDE:
_SetViewCursor(view, kUpDownCursor);
break;
}
} else if (rotation < 90.0) {
switch (fSide) {
case LEFT_SIDE:
case RIGHT_SIDE:
_SetViewCursor(view, kLeftBottomRightTopCursor);
break;
case TOP_SIDE:
case BOTTOM_SIDE:
_SetViewCursor(view, kLeftTopRightBottomCursor);
break;
}
} else if (rotation < 135.0) {
switch (fSide) {
case LEFT_SIDE:
case RIGHT_SIDE:
_SetViewCursor(view, kUpDownCursor);
break;
case TOP_SIDE:
case BOTTOM_SIDE:
_SetViewCursor(view, kLeftRightCursor);
break;
}
} else {
switch (fSide) {
case LEFT_SIDE:
case RIGHT_SIDE:
_SetViewCursor(view, kLeftTopRightBottomCursor);
break;
case TOP_SIDE:
case BOTTOM_SIDE:
_SetViewCursor(view, kLeftBottomRightTopCursor);
break;
}
}
}
// ActionName
const char*
DragSideState::ActionName() const
{
return "Scale";
}
// ActionNameIndex
uint32
DragSideState::ActionNameIndex() const
{
return SCALE;
}
// #pragma mark - DragBoxState
// SetOrigin
void
DragBoxState::SetOrigin(BPoint origin)
{
fOldTranslation = fParent->Translation();
DragState::SetOrigin(origin);
}
// DragTo
void
DragBoxState::DragTo(BPoint current, uint32 modifiers)
{
BPoint offset = current - fOrigin;
BPoint newTranslation = fOldTranslation + offset;
if (modifiers & B_SHIFT_KEY) {
if (fabs(offset.x) > fabs(offset.y))
newTranslation.y = fOldTranslation.y;
else
newTranslation.x = fOldTranslation.x;
}
fParent->TranslateBy(newTranslation - fParent->Translation());
}
// UpdateViewCursor
void
DragBoxState::UpdateViewCursor(BView* view, BPoint current) const
{
_SetViewCursor(view, kMoveCursor);
}
// ActionName
const char*
DragBoxState::ActionName() const
{
return "Move";
}
// ActionNameIndex
uint32
DragBoxState::ActionNameIndex() const
{
return MOVE;
}
// #pragma mark - RotateBoxState
// constructor
RotateBoxState::RotateBoxState(TransformBox* parent)
: DragState(parent),
fOldAngle(0.0)
{
}
// SetOrigin
void
RotateBoxState::SetOrigin(BPoint origin)
{
DragState::SetOrigin(origin);
fOldAngle = fParent->LocalRotation();
}
// DragTo
void
RotateBoxState::DragTo(BPoint current, uint32 modifiers)
{
double angle = calc_angle(fParent->Pivot(), fOrigin, current);
if (modifiers & B_SHIFT_KEY) {
if (angle < 0.0)
angle -= 22.5;
else
angle += 22.5;
angle = 45.0 * ((int32)angle / 45);
}
double newAngle = fOldAngle + angle;
fParent->RotateBy(newAngle - fParent->LocalRotation());
}
// UpdateViewCursor
void
RotateBoxState::UpdateViewCursor(BView* view, BPoint current) const
{
BPoint origin(fParent->Pivot());
fParent->TransformToCanvas(origin);
fParent->TransformToCanvas(current);
BPoint from = origin + BPoint(sinf(22.5 * 180.0 / PI) * 50.0,
-cosf(22.5 * 180.0 / PI) * 50.0);
float rotation = calc_angle(origin, from, current) + 180.0;
if (rotation < 45.0) {
_SetViewCursor(view, kRotateLCursor);
} else if (rotation < 90.0) {
_SetViewCursor(view, kRotateLTCursor);
} else if (rotation < 135.0) {
_SetViewCursor(view, kRotateTCursor);
} else if (rotation < 180.0) {
_SetViewCursor(view, kRotateRTCursor);
} else if (rotation < 225.0) {
_SetViewCursor(view, kRotateRCursor);
} else if (rotation < 270.0) {
_SetViewCursor(view, kRotateRBCursor);
} else if (rotation < 315.0) {
_SetViewCursor(view, kRotateBCursor);
} else {
_SetViewCursor(view, kRotateLBCursor);
}
}
// ActionName
const char*
RotateBoxState::ActionName() const
{
return "Rotate";
}
// ActionNameIndex
uint32
RotateBoxState::ActionNameIndex() const
{
return ROTATE;
}
// #pragma mark - OffsetCenterState
// SetOrigin
void
OffsetCenterState::SetOrigin(BPoint origin)
{
fParent->InverseTransform(&origin);
DragState::SetOrigin(origin);
}
// DragTo
void
OffsetCenterState::DragTo(BPoint current, uint32 modifiers)
{
fParent->InverseTransform(&current);
fParent->OffsetPivot(current - fOrigin);
fOrigin = current;
}
// UpdateViewCursor
void
OffsetCenterState::UpdateViewCursor(BView* view, BPoint current) const
{
_SetViewCursor(view, kPathMoveCursor);
}
// ActionName
const char*
OffsetCenterState::ActionName() const
{
return "Move Pivot";
}
// ActionNameIndex
uint32
OffsetCenterState::ActionNameIndex() const
{
return MOVE_PIVOT;
}

View File

@ -0,0 +1,163 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef TRANSFORM_BOX_STATES_H
#define TRANSFORM_BOX_STATES_H
#include <Point.h>
#include <agg_trans_affine.h>
class BView;
class TransformBox;
// TODO: remove and replace with translation string indices once multiple
// languages are supported
#define TRANSFORMATION 0
#define ROTATE 0
#define MOVE 0
#define SCALE 0
#define MOVE_PIVOT 0
//
// base class
class DragState {
public:
DragState(TransformBox* parent);
virtual ~DragState() {}
virtual void SetOrigin(BPoint origin);
virtual void DragTo(BPoint current, uint32 modifiers) = 0;
virtual void UpdateViewCursor(BView* view, BPoint current) const = 0;
virtual const char* ActionName() const;
virtual uint32 ActionNameIndex() const;
protected:
void _SetViewCursor(BView* view,
const uchar* cursorData) const;
BPoint fOrigin;
TransformBox* fParent;
};
// scaling states
class DragCornerState : public DragState {
public:
enum {
LEFT_TOP_CORNER,
RIGHT_TOP_CORNER,
LEFT_BOTTOM_CORNER,
RIGHT_BOTTOM_CORNER,
};
DragCornerState(TransformBox* parent,
uint32 corner);
virtual ~DragCornerState() {}
virtual void SetOrigin(BPoint origin);
virtual void DragTo(BPoint current, uint32 modifiers);
virtual void UpdateViewCursor(BView* view, BPoint current) const;
virtual const char* ActionName() const;
virtual uint32 ActionNameIndex() const;
private:
uint32 fCorner;
float fXOffsetFromCorner;
float fYOffsetFromCorner;
double fOldXScale;
double fOldYScale;
double fOldWidth;
double fOldHeight;
agg::trans_affine fMatrix;
BPoint fOldOffset;
};
class DragSideState : public DragState {
public:
enum {
LEFT_SIDE,
TOP_SIDE,
RIGHT_SIDE,
BOTTOM_SIDE,
};
DragSideState(TransformBox* parent,
uint32 side);
virtual ~DragSideState() {}
virtual void SetOrigin(BPoint origin);
virtual void DragTo(BPoint current, uint32 modifiers);
virtual void UpdateViewCursor(BView* view, BPoint current) const;
virtual const char* ActionName() const;
virtual uint32 ActionNameIndex() const;
private:
uint32 fSide;
float fOffsetFromSide;
double fOldXScale;
double fOldYScale;
double fOldSideDist;
agg::trans_affine fMatrix;
BPoint fOldOffset;
};
// translate state
class DragBoxState : public DragState {
public:
DragBoxState(TransformBox* parent)
: DragState(parent) {}
virtual ~DragBoxState() {}
virtual void SetOrigin(BPoint origin);
virtual void DragTo(BPoint current, uint32 modifiers);
virtual void UpdateViewCursor(BView* view, BPoint current) const;
virtual const char* ActionName() const;
virtual uint32 ActionNameIndex() const;
private:
BPoint fOldTranslation;
};
// rotate state
class RotateBoxState : public DragState {
public:
RotateBoxState(TransformBox* parent);
virtual ~RotateBoxState() {}
virtual void SetOrigin(BPoint origin);
virtual void DragTo(BPoint current, uint32 modifiers);
virtual void UpdateViewCursor(BView* view, BPoint current) const;
virtual const char* ActionName() const;
virtual uint32 ActionNameIndex() const;
private:
double fOldAngle;
};
// offset center state
class OffsetCenterState : public DragState {
public:
OffsetCenterState(TransformBox* parent)
: DragState(parent) {}
virtual ~OffsetCenterState() {}
virtual void SetOrigin(BPoint origin);
virtual void DragTo(BPoint current, uint32 modifiers);
virtual void UpdateViewCursor(BView* view, BPoint current) const;
virtual const char* ActionName() const;
virtual uint32 ActionNameIndex() const;
};
#endif // TRANSFORM_BOX_STATES_H

View File

@ -0,0 +1,154 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "TransformCommand.h"
#include <stdio.h>
// constructor
TransformCommand::TransformCommand(BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale,
const char* actionName,
uint32 nameIndex)
: Command(),
fOldPivot(pivot),
fOldTranslation(translation),
fOldRotation(rotation),
fOldXScale(xScale),
fOldYScale(yScale),
fNewPivot(pivot),
fNewTranslation(translation),
fNewRotation(rotation),
fNewXScale(xScale),
fNewYScale(yScale),
fName(actionName),
fNameIndex(nameIndex)
{
}
// constructor
TransformCommand::TransformCommand(const char* actionName,
uint32 nameIndex)
: Command(),
fOldPivot(B_ORIGIN),
fOldTranslation(B_ORIGIN),
fOldRotation(0.0),
fOldXScale(1.0),
fOldYScale(1.0),
fNewPivot(B_ORIGIN),
fNewTranslation(B_ORIGIN),
fNewRotation(0.0),
fNewXScale(1.0),
fNewYScale(1.0),
fName(actionName),
fNameIndex(nameIndex)
{
}
// destructor
TransformCommand::~TransformCommand()
{
}
// InitCheck
status_t
TransformCommand::InitCheck()
{
if ((fNewPivot != fOldPivot
|| fNewTranslation != fOldTranslation
|| fNewRotation != fOldRotation
|| fNewXScale != fOldXScale
|| fNewYScale != fOldYScale))
return B_OK;
else
return B_NO_INIT;
}
// Perform
status_t
TransformCommand::Perform()
{
// objects are already transformed
return B_OK;
}
// Undo
status_t
TransformCommand::Undo()
{
status_t status = InitCheck();
if (status >= B_OK) {
_SetTransformation(fOldPivot - fNewPivot,
fOldTranslation - fNewTranslation,
fOldRotation - fNewRotation,
fOldXScale - fNewXScale,
fOldYScale - fNewYScale);
}
return status;
}
// Redo
status_t
TransformCommand::Redo()
{
status_t status = InitCheck();
if (status >= B_OK) {
_SetTransformation(fNewPivot - fOldPivot,
fNewTranslation - fOldTranslation,
fNewRotation - fOldRotation,
fNewXScale - fOldXScale,
fNewYScale - fOldYScale);
}
return status;
}
// GetName
void
TransformCommand::GetName(BString& name)
{
name << _GetString(fNameIndex, fName.String());
}
// SetNewTransformation
void
TransformCommand::SetNewTransformation(BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale)
{
fNewPivot = pivot;
fNewTranslation = translation;
fNewRotation = rotation;
fNewXScale = xScale;
fNewYScale = yScale;
}
// SetNewTranslation
void
TransformCommand::SetNewTranslation(BPoint translation)
{
// NOTE: convinience method for nudging
fNewTranslation = translation;
}
// SetName
void
TransformCommand::SetName(const char* actionName, uint32 nameIndex)
{
fName.SetTo(actionName);
fNameIndex = nameIndex;
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef TRANSFORM_COMMAND_H
#define TRANSFORM_COMMAND_H
#include <Point.h>
#include <String.h>
#include "Command.h"
class TransformCommand : public Command {
public:
TransformCommand(BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale,
const char* actionName,
uint32 nameIndex);
TransformCommand(const char* actionName,
uint32 nameIndex);
virtual ~TransformCommand();
// Command interface
virtual status_t InitCheck();
virtual status_t Perform();
virtual status_t Undo();
virtual status_t Redo();
virtual void GetName(BString& name);
// TransformCommand
void SetNewTransformation(BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale);
void SetNewTranslation(BPoint translation);
// convinience for "nudging"
void SetName(const char* actionName,
uint32 nameIndex);
protected:
virtual status_t _SetTransformation(BPoint pivotDiff,
BPoint translationDiff,
double rotationDiff,
double xScaleDiff,
double yScaleDiff) const = 0;
BPoint fOldPivot;
BPoint fOldTranslation;
double fOldRotation;
double fOldXScale;
double fOldYScale;
BPoint fNewPivot;
BPoint fNewTranslation;
double fNewRotation;
double fNewXScale;
double fNewYScale;
BString fName;
uint32 fNameIndex;
};
#endif // TRANSFORM_COMMAND_H

View File

@ -0,0 +1,181 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "TransformShapesBox.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include "CanvasView.h"
#include "Shape.h"
#include "StateView.h"
//#include "TransformShapesCommand.h"
using std::nothrow;
// constructor
TransformShapesBox::TransformShapesBox(CanvasView* view,
const Shape** shapes,
int32 count)
: TransformBox(view, BRect(0.0, 0.0, 1.0, 1.0)),
fCanvasView(view),
fShapes(shapes && count > 0 ? new Shape*[count] : NULL),
fCount(count),
fOriginals(NULL),
fParentTransform()
{
if (fShapes) {
// allocate storage for the current transformations
// of each object
fOriginals = new double[fCount * 6];
memcpy(fShapes, shapes, fCount * sizeof(Shape*));
for (int32 i = 0; i < fCount; i++) {
if (fShapes[i])
fShapes[i]->AddObserver(this);
}
// trigger init
ObjectChanged(fShapes[0]);
} else {
SetBox(BRect(0, 0, -1, -1));
}
}
// destructor
TransformShapesBox::~TransformShapesBox()
{
if (fShapes) {
for (int32 i = 0; i < fCount; i++) {
if (fShapes[i])
fShapes[i]->RemoveObserver(this);
}
delete[] fShapes;
}
delete[] fOriginals;
}
// Update
void
TransformShapesBox::Update(bool deep)
{
BRect r = Bounds();
TransformBox::Update(deep);
BRect dirty(r | Bounds());
dirty.InsetBy(-8, -8);
fView->Invalidate(dirty);
if (!deep || !fShapes)
return;
for (int32 i = 0; i < fCount; i++) {
if (!fShapes[i])
continue;
fShapes[i]->RemoveObserver(this);
fShapes[i]->SuspendNotifications(true);
// reset the objects transformation to the saved state
fShapes[i]->LoadFrom(&fOriginals[i * 6]);
// combined with the current transformation
fShapes[i]->Multiply(*this);
fShapes[i]->SuspendNotifications(false);
fShapes[i]->AddObserver(this);
}
}
// ObjectChanged
void
TransformShapesBox::ObjectChanged(const Observable* object)
{
if (!fView->LockLooper())
return;
fParentTransform.Reset();
// figure out bounds and store initial transformations
BRect box(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN);
for (int32 i = 0; i < fCount; i++) {
if (!fShapes[i])
continue;
box = box | fShapes[i]->Bounds();
fShapes[i]->StoreTo(&fOriginals[i * 6]);
}
Reset();
SetBox(box);
fView->UnlockLooper();
}
// Perform
Command*
TransformShapesBox::Perform()
{
return NULL;
}
// Cancel
Command*
TransformShapesBox::Cancel()
{
SetTransformation(B_ORIGIN, B_ORIGIN, 0.0, 1.0, 1.0);
return NULL;
}
// TransformFromCanvas
void
TransformShapesBox::TransformFromCanvas(BPoint& point) const
{
fParentTransform.InverseTransform(&point);
fCanvasView->ConvertFromCanvas(&point);
}
// TransformToCanvas
void
TransformShapesBox::TransformToCanvas(BPoint& point) const
{
fCanvasView->ConvertToCanvas(&point);
fParentTransform.Transform(&point);
}
// ViewSpaceRotation
double
TransformShapesBox::ViewSpaceRotation() const
{
Transformable t(*this);
t.Multiply(fParentTransform);
return t.rotation() * 180.0 / PI;
}
// MakeAction
TransformCommand*
TransformShapesBox::MakeAction(const char* actionName, uint32 nameIndex) const
{
// return new TransformShapesCommand(fShapes, fCount,
//
// Pivot(),
// Translation(),
// LocalRotation(),
// LocalXScale(),
// LocalYScale(),
//
// actionName,
// nameIndex);
return NULL;
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef TRANSFORM_SHAPES_BOX_H
#define TRANSFORM_SHAPES_BOX_H
#include "TransformBox.h"
class CanvasView;
class Shape;
class TransformShapesBox : public TransformBox {
public:
TransformShapesBox(CanvasView* view,
const Shape** objects,
int32 count);
virtual ~TransformShapesBox();
// Observer interface (Manipulator is an Observer)
virtual void ObjectChanged(const Observable* object);
// TransformBox interface
virtual void Update(bool deep = true);
virtual void TransformFromCanvas(BPoint& point) const;
virtual void TransformToCanvas(BPoint& point) const;
virtual double ViewSpaceRotation() const;
virtual TransformCommand* MakeAction(const char* actionName,
uint32 nameIndex) const;
// TransformShapesBox
Command* Perform();
Command* Cancel();
Shape** Shapes() const
{ return fShapes; }
int32 CountShapes() const
{ return fCount; }
private:
CanvasView* fCanvasView;
Shape** fShapes;
int32 fCount;
// saves the transformable objects transformation states
// prior to this transformation
double* fOriginals;
Transformable fParentTransform;
};
#endif // TRANSFORM_SHAPES_BOX_H

View File

@ -0,0 +1,312 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "Transformable.h"
#include <stdio.h>
#include <string.h>
#include "support.h"
// constructor
Transformable::Transformable()
: agg::trans_affine()
{
}
// copy constructor
Transformable::Transformable(const Transformable& other)
: agg::trans_affine(other)
{
}
// destructor
Transformable::~Transformable()
{
}
// StoreTo
void
Transformable::StoreTo(double matrix[6]) const
{
store_to(matrix);
}
// LoadFrom
void
Transformable::LoadFrom(double matrix[6])
{
// before calling the potentially heavy TransformationChanged()
// hook function, make sure that the transformation
// really changed
Transformable t;
t.load_from(matrix);
if (*this != t) {
load_from(matrix);
TransformationChanged();
}
}
// SetTransform
void
Transformable::SetTransform(const Transformable& other)
{
if (*this != other) {
*this = other;
TransformationChanged();
}
}
// operator=
Transformable&
Transformable::operator=(const Transformable& other)
{
if (other != *this) {
reset();
multiply(other);
TransformationChanged();
}
return *this;
}
// Multiply
Transformable&
Transformable::Multiply(const Transformable& other)
{
if (!other.IsIdentity()) {
multiply(other);
TransformationChanged();
}
return *this;
}
// Reset
void
Transformable::Reset()
{
reset();
}
// Invert
void
Transformable::Invert()
{
invert();
}
// IsIdentity
bool
Transformable::IsIdentity() const
{
double m[6];
store_to(m);
if (m[0] == 1.0 &&
m[1] == 0.0 &&
m[2] == 0.0 &&
m[3] == 1.0 &&
m[4] == 0.0 &&
m[5] == 0.0)
return true;
return false;
}
// IsTranslationOnly
bool
Transformable::IsTranslationOnly() const
{
double m[6];
store_to(m);
if (m[0] == 1.0 &&
m[1] == 0.0 &&
m[2] == 0.0 &&
m[3] == 1.0)
return true;
return false;
}
// IsNotDistorted
bool
Transformable::IsNotDistorted() const
{
double m[6];
store_to(m);
return (m[0] == m[3]);
}
// IsValid
bool
Transformable::IsValid() const
{
double m[6];
store_to(m);
return ((m[0] * m[3] - m[1] * m[2]) != 0.0);
}
// operator==
bool
Transformable::operator==(const Transformable& other) const
{
double m1[6];
other.store_to(m1);
double m2[6];
store_to(m2);
if (m1[0] == m2[0] &&
m1[1] == m2[1] &&
m1[2] == m2[2] &&
m1[3] == m2[3] &&
m1[4] == m2[4] &&
m1[5] == m2[5])
return true;
return false;
}
// operator!=
bool
Transformable::operator!=(const Transformable& other) const
{
return !(*this == other);
}
// Transform
void
Transformable::Transform(double* x, double* y) const
{
transform(x, y);
}
// Transform
void
Transformable::Transform(BPoint* point) const
{
if (point) {
double x = point->x;
double y = point->y;
transform(&x, &y);
point->x = x;
point->y = y;
}
}
// Transform
BPoint
Transformable::Transform(const BPoint& point) const
{
BPoint p(point);
Transform(&p);
return p;
}
// InverseTransform
void
Transformable::InverseTransform(double* x, double* y) const
{
inverse_transform(x, y);
}
// InverseTransform
void
Transformable::InverseTransform(BPoint* point) const
{
if (point) {
double x = point->x;
double y = point->y;
inverse_transform(&x, &y);
point->x = x;
point->y = y;
}
}
// InverseTransform
BPoint
Transformable::InverseTransform(const BPoint& point) const
{
BPoint p(point);
InverseTransform(&p);
return p;
}
// TransformBounds
BRect
Transformable::TransformBounds(BRect bounds) const
{
if (bounds.IsValid()) {
BPoint lt(bounds.left, bounds.top);
BPoint rt(bounds.right, bounds.top);
BPoint lb(bounds.left, bounds.bottom);
BPoint rb(bounds.right, bounds.bottom);
Transform(&lt);
Transform(&rt);
Transform(&lb);
Transform(&rb);
return BRect(floorf(min4(lt.x, rt.x, lb.x, rb.x)),
floorf(min4(lt.y, rt.y, lb.y, rb.y)),
ceilf(max4(lt.x, rt.x, lb.x, rb.x)),
ceilf(max4(lt.y, rt.y, lb.y, rb.y)));
}
return bounds;
}
// TranslateBy
void
Transformable::TranslateBy(BPoint offset)
{
if (offset.x != 0.0 || offset.y != 0.0) {
multiply(agg::trans_affine_translation(offset.x, offset.y));
TransformationChanged();
}
}
// RotateBy
void
Transformable::RotateBy(BPoint origin, double degrees)
{
if (degrees != 0.0) {
multiply(agg::trans_affine_translation(-origin.x, -origin.y));
multiply(agg::trans_affine_rotation(degrees * (PI / 180.0)));
multiply(agg::trans_affine_translation(origin.x, origin.y));
TransformationChanged();
}
}
// ScaleBy
void
Transformable::ScaleBy(BPoint origin, double xScale, double yScale)
{
if (xScale != 1.0 || yScale != 1.0) {
multiply(agg::trans_affine_translation(-origin.x, -origin.y));
multiply(agg::trans_affine_scaling(xScale, yScale));
multiply(agg::trans_affine_translation(origin.x, origin.y));
TransformationChanged();
}
}
// ShearBy
void
Transformable::ShearBy(BPoint origin, double xShear, double yShear)
{
if (xShear != 0.0 || yShear != 0.0) {
multiply(agg::trans_affine_translation(-origin.x, -origin.y));
multiply(agg::trans_affine_skewing(xShear, yShear));
multiply(agg::trans_affine_translation(origin.x, origin.y));
TransformationChanged();
}
}
// TransformationChanged
void
Transformable::TransformationChanged()
{
// default implementation doesn't care
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef TRANSFORMABLE_H
#define TRANSFORMABLE_H
#include <Rect.h>
#include <agg_trans_affine.h>
#include "Observable.h"
class Transformable : public agg::trans_affine {
public:
Transformable();
Transformable(const Transformable& other);
virtual ~Transformable();
void StoreTo(double matrix[6]) const;
void LoadFrom(double matrix[6]);
// set to or combine with other matrix
void SetTransform(const Transformable& other);
Transformable& operator=(const Transformable& other);
Transformable& Multiply(const Transformable& other);
virtual void Reset();
void Invert();
bool IsIdentity() const;
bool IsTranslationOnly() const;
bool IsNotDistorted() const;
bool IsValid() const;
bool operator==(const Transformable& other) const;
bool operator!=(const Transformable& other) const;
// transforms coordiantes
void Transform(double* x, double* y) const;
void Transform(BPoint* point) const;
BPoint Transform(const BPoint& point) const;
void InverseTransform(double* x, double* y) const;
void InverseTransform(BPoint* point) const;
BPoint InverseTransform(const BPoint& point) const;
// transforms the rectangle "bounds" and
// returns the *bounding box* of that
BRect TransformBounds(BRect bounds) const;
// some convenience functions
virtual void TranslateBy(BPoint offset);
virtual void RotateBy(BPoint origin, double degrees);
virtual void ScaleBy(BPoint origin, double xScale, double yScale);
virtual void ShearBy(BPoint origin, double xShear, double yShear);
virtual void TransformationChanged();
// hook function that is called when the transformation
// is changed for some reason
};
#endif // TRANSFORMABLE_H