* 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:
parent
ea3746798b
commit
0e684ccfed
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
227
src/apps/icon-o-matic/transformable/ChannelTransform.cpp
Normal file
227
src/apps/icon-o-matic/transformable/ChannelTransform.cpp
Normal 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();
|
||||
}
|
||||
|
67
src/apps/icon-o-matic/transformable/ChannelTransform.h
Normal file
67
src/apps/icon-o-matic/transformable/ChannelTransform.h
Normal 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
|
||||
|
594
src/apps/icon-o-matic/transformable/TransformBox.cpp
Normal file
594
src/apps/icon-o-matic/transformable/TransformBox.cpp
Normal 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(<);
|
||||
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(<);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
124
src/apps/icon-o-matic/transformable/TransformBox.h
Normal file
124
src/apps/icon-o-matic/transformable/TransformBox.h
Normal 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
|
615
src/apps/icon-o-matic/transformable/TransformBoxStates.cpp
Normal file
615
src/apps/icon-o-matic/transformable/TransformBoxStates.cpp
Normal 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(¤t);
|
||||
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;
|
||||
}
|
||||
|
||||
|
163
src/apps/icon-o-matic/transformable/TransformBoxStates.h
Normal file
163
src/apps/icon-o-matic/transformable/TransformBoxStates.h
Normal 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
|
154
src/apps/icon-o-matic/transformable/TransformCommand.cpp
Normal file
154
src/apps/icon-o-matic/transformable/TransformCommand.cpp
Normal 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;
|
||||
}
|
||||
|
78
src/apps/icon-o-matic/transformable/TransformCommand.h
Normal file
78
src/apps/icon-o-matic/transformable/TransformCommand.h
Normal 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
|
181
src/apps/icon-o-matic/transformable/TransformShapesBox.cpp
Normal file
181
src/apps/icon-o-matic/transformable/TransformShapesBox.cpp
Normal 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;
|
||||
}
|
60
src/apps/icon-o-matic/transformable/TransformShapesBox.h
Normal file
60
src/apps/icon-o-matic/transformable/TransformShapesBox.h
Normal 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
|
||||
|
312
src/apps/icon-o-matic/transformable/Transformable.cpp
Normal file
312
src/apps/icon-o-matic/transformable/Transformable.cpp
Normal 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(<);
|
||||
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
|
||||
}
|
||||
|
68
src/apps/icon-o-matic/transformable/Transformable.h
Normal file
68
src/apps/icon-o-matic/transformable/Transformable.h
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user