* 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:
@@ -26,6 +26,7 @@ local sourceDirs =
|
|||||||
shape
|
shape
|
||||||
shape/commands
|
shape/commands
|
||||||
style
|
style
|
||||||
|
transformable
|
||||||
transformer
|
transformer
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -138,6 +139,13 @@ Application Icon-O-Matic :
|
|||||||
SetGradientCommand.cpp
|
SetGradientCommand.cpp
|
||||||
Style.cpp
|
Style.cpp
|
||||||
StyleManager.cpp
|
StyleManager.cpp
|
||||||
|
# transformable
|
||||||
|
ChannelTransform.cpp
|
||||||
|
Transformable.cpp
|
||||||
|
TransformBox.cpp
|
||||||
|
TransformBoxStates.cpp
|
||||||
|
TransformCommand.cpp
|
||||||
|
TransformShapesBox.cpp
|
||||||
# transformer
|
# transformer
|
||||||
AffineTransformer.cpp
|
AffineTransformer.cpp
|
||||||
ContourTransformer.cpp
|
ContourTransformer.cpp
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "SwatchGroup.h"
|
#include "SwatchGroup.h"
|
||||||
#include "TransformerFactory.h"
|
#include "TransformerFactory.h"
|
||||||
#include "TransformerListView.h"
|
#include "TransformerListView.h"
|
||||||
|
#include "TransformShapesBox.h"
|
||||||
|
|
||||||
// TODO: just for testing
|
// TODO: just for testing
|
||||||
#include "AffineTransformer.h"
|
#include "AffineTransformer.h"
|
||||||
@@ -144,6 +145,25 @@ case MSG_SHAPE_SELECTED: {
|
|||||||
fPathListView->SetCurrentShape(shape);
|
fPathListView->SetCurrentShape(shape);
|
||||||
fStyleListView->SetCurrentShape(shape);
|
fStyleListView->SetCurrentShape(shape);
|
||||||
fTransformerListView->SetShape(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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,11 @@
|
|||||||
using std::nothrow;
|
using std::nothrow;
|
||||||
|
|
||||||
class StyleHandler {
|
class StyleHandler {
|
||||||
|
struct StyleItem {
|
||||||
|
Style* style;
|
||||||
|
Transformation transformation;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StyleHandler(GammaTable& gammaTable)
|
StyleHandler(GammaTable& gammaTable)
|
||||||
: fStyles(20),
|
: fStyles(20),
|
||||||
@@ -35,13 +40,20 @@ class StyleHandler {
|
|||||||
fColor(0, 0, 0, 0)
|
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
|
bool is_solid(unsigned styleIndex) const
|
||||||
{
|
{
|
||||||
Style* style = (Style*)fStyles.ItemAt(styleIndex);
|
StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
|
||||||
if (!style)
|
if (!styleItem)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return style->Gradient() == NULL;
|
return styleItem->style->Gradient() == NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const agg::rgba8& color(unsigned styleIndex);
|
const agg::rgba8& color(unsigned styleIndex);
|
||||||
@@ -49,9 +61,21 @@ class StyleHandler {
|
|||||||
void generate_span(agg::rgba8* span, int x, int y,
|
void generate_span(agg::rgba8* span, int x, int y,
|
||||||
unsigned len, unsigned styleIndex);
|
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:
|
private:
|
||||||
@@ -59,7 +83,7 @@ private:
|
|||||||
void _GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
|
void _GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
|
||||||
GradientFunction function,
|
GradientFunction function,
|
||||||
const agg::rgba8* gradientColors,
|
const agg::rgba8* gradientColors,
|
||||||
agg::trans_affine& gradientTransform);
|
Transformation& gradientTransform);
|
||||||
|
|
||||||
BList fStyles;
|
BList fStyles;
|
||||||
GammaTable& fGammaTable;
|
GammaTable& fGammaTable;
|
||||||
@@ -71,13 +95,13 @@ private:
|
|||||||
const agg::rgba8&
|
const agg::rgba8&
|
||||||
StyleHandler::color(unsigned styleIndex)
|
StyleHandler::color(unsigned styleIndex)
|
||||||
{
|
{
|
||||||
Style* style = (Style*)fStyles.ItemAt(styleIndex);
|
StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
|
||||||
if (!style) {
|
if (!styleItem) {
|
||||||
printf("no style at index: %d!\n", styleIndex);
|
printf("no style at index: %d!\n", styleIndex);
|
||||||
return fTransparent;
|
return fTransparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rgb_color& c = style->Color();
|
const rgb_color& c = styleItem->style->Color();
|
||||||
fColor = agg::rgba8(fGammaTable.dir(c.red),
|
fColor = agg::rgba8(fGammaTable.dir(c.red),
|
||||||
fGammaTable.dir(c.green),
|
fGammaTable.dir(c.green),
|
||||||
fGammaTable.dir(c.blue),
|
fGammaTable.dir(c.blue),
|
||||||
@@ -91,55 +115,52 @@ void
|
|||||||
StyleHandler::generate_span(agg::rgba8* span, int x, int y,
|
StyleHandler::generate_span(agg::rgba8* span, int x, int y,
|
||||||
unsigned len, unsigned styleIndex)
|
unsigned len, unsigned styleIndex)
|
||||||
{
|
{
|
||||||
Style* style = (Style*)fStyles.ItemAt(styleIndex);
|
StyleItem* styleItem = (StyleItem*)fStyles.ItemAt(styleIndex);
|
||||||
if (!style || !style->Gradient()) {
|
if (!styleItem || !styleItem->style->Gradient()) {
|
||||||
printf("no style/gradient at index: %d!\n", styleIndex);
|
printf("no style/gradient at index: %d!\n", styleIndex);
|
||||||
// TODO: memset() span?
|
// TODO: memset() span?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Style* style = styleItem->style;
|
||||||
Gradient* gradient = style->Gradient();
|
Gradient* gradient = style->Gradient();
|
||||||
const agg::rgba8* colors = style->Colors();
|
const agg::rgba8* colors = style->Colors();
|
||||||
|
|
||||||
agg::trans_affine transformation;
|
|
||||||
// TODO: construct the gradient transformation here
|
|
||||||
// remember to invert() it!
|
|
||||||
|
|
||||||
switch (gradient->Type()) {
|
switch (gradient->Type()) {
|
||||||
case GRADIENT_LINEAR: {
|
case GRADIENT_LINEAR: {
|
||||||
agg::gradient_x function;
|
agg::gradient_x function;
|
||||||
_GenerateGradient(span, x, y, len, function, colors,
|
_GenerateGradient(span, x, y, len, function, colors,
|
||||||
transformation);
|
styleItem->transformation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GRADIENT_CIRCULAR: {
|
case GRADIENT_CIRCULAR: {
|
||||||
agg::gradient_radial function;
|
agg::gradient_radial function;
|
||||||
_GenerateGradient(span, x, y, len, function, colors,
|
_GenerateGradient(span, x, y, len, function, colors,
|
||||||
transformation);
|
styleItem->transformation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GRADIENT_DIAMONT: {
|
case GRADIENT_DIAMONT: {
|
||||||
agg::gradient_diamond function;
|
agg::gradient_diamond function;
|
||||||
_GenerateGradient(span, x, y, len, function, colors,
|
_GenerateGradient(span, x, y, len, function, colors,
|
||||||
transformation);
|
styleItem->transformation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GRADIENT_CONIC: {
|
case GRADIENT_CONIC: {
|
||||||
agg::gradient_conic function;
|
agg::gradient_conic function;
|
||||||
_GenerateGradient(span, x, y, len, function, colors,
|
_GenerateGradient(span, x, y, len, function, colors,
|
||||||
transformation);
|
styleItem->transformation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GRADIENT_XY: {
|
case GRADIENT_XY: {
|
||||||
agg::gradient_xy function;
|
agg::gradient_xy function;
|
||||||
_GenerateGradient(span, x, y, len, function, colors,
|
_GenerateGradient(span, x, y, len, function, colors,
|
||||||
transformation);
|
styleItem->transformation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GRADIENT_SQRT_XY: {
|
case GRADIENT_SQRT_XY: {
|
||||||
agg::gradient_sqrt_xy function;
|
agg::gradient_sqrt_xy function;
|
||||||
_GenerateGradient(span, x, y, len, function, colors,
|
_GenerateGradient(span, x, y, len, function, colors,
|
||||||
transformation);
|
styleItem->transformation);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +172,7 @@ void
|
|||||||
StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
|
StyleHandler::_GenerateGradient(agg::rgba8* span, int x, int y, unsigned len,
|
||||||
GradientFunction function,
|
GradientFunction function,
|
||||||
const agg::rgba8* gradientColors,
|
const agg::rgba8* gradientColors,
|
||||||
agg::trans_affine& gradientTransform)
|
Transformation& gradientTransform)
|
||||||
|
|
||||||
{
|
{
|
||||||
typedef agg::pod_auto_array<agg::rgba8, 256> ColorArray;
|
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));
|
fBaseRendererPre.clear(agg::rgba8(0, 0, 0, 0));
|
||||||
|
|
||||||
|
//bigtime_t start = system_time();
|
||||||
StyleHandler styleHandler(fGammaTable);
|
StyleHandler styleHandler(fGammaTable);
|
||||||
|
|
||||||
fRasterizer.reset();
|
fRasterizer.reset();
|
||||||
@@ -272,20 +294,22 @@ IconRenderer::_Render(const BRect& r)
|
|||||||
for (int32 i = 0; i < shapeCount; i++) {
|
for (int32 i = 0; i < shapeCount; i++) {
|
||||||
Shape* shape = fIcon->Shapes()->ShapeAtFast(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");
|
printf("IconRenderer::_Render() - out of memory\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fRasterizer.styles(i, -1);
|
fRasterizer.styles(i, -1);
|
||||||
|
|
||||||
if (fGlobalTransform.is_identity()) {
|
|
||||||
fRasterizer.add_path(shape->VertexSource());
|
|
||||||
} else {
|
|
||||||
agg::conv_transform<VertexSource, Transformation>
|
agg::conv_transform<VertexSource, Transformation>
|
||||||
scaledPath(shape->VertexSource(), fGlobalTransform);
|
scaledPath(shape->VertexSource(), transform);
|
||||||
fRasterizer.add_path(scaledPath);
|
fRasterizer.add_path(scaledPath);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
agg::render_scanlines_compound(fRasterizer,
|
agg::render_scanlines_compound(fRasterizer,
|
||||||
fScanline,
|
fScanline,
|
||||||
@@ -296,5 +320,8 @@ IconRenderer::_Render(const BRect& r)
|
|||||||
|
|
||||||
if (fGammaTable.gamma() != 1.0)
|
if (fGammaTable.gamma() != 1.0)
|
||||||
fPixelFormat.apply_gamma_inv(fGammaTable);
|
fPixelFormat.apply_gamma_inv(fGammaTable);
|
||||||
|
|
||||||
|
//if (fRenderingBuffer.width() == 64)
|
||||||
|
//printf("rendering 64x64: %lld\n", system_time() - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ ShapeListener::~ShapeListener()
|
|||||||
// constructor
|
// constructor
|
||||||
Shape::Shape(::Style* style)
|
Shape::Shape(::Style* style)
|
||||||
: IconObject("<shape>"),
|
: IconObject("<shape>"),
|
||||||
|
Transformable(),
|
||||||
Observer(),
|
Observer(),
|
||||||
PathContainerListener(),
|
PathContainerListener(),
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ Shape::Shape(::Style* style)
|
|||||||
// constructor
|
// constructor
|
||||||
Shape::Shape(const Shape& other)
|
Shape::Shape(const Shape& other)
|
||||||
: IconObject(other),
|
: IconObject(other),
|
||||||
|
Transformable(other),
|
||||||
Observer(),
|
Observer(),
|
||||||
PathContainerListener(),
|
PathContainerListener(),
|
||||||
|
|
||||||
@@ -94,6 +96,16 @@ Shape::~Shape()
|
|||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
// TransformationChanged
|
||||||
|
void
|
||||||
|
Shape::TransformationChanged()
|
||||||
|
{
|
||||||
|
// TODO: notify appearance change
|
||||||
|
_NotifyRerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
// ObjectChanged
|
// ObjectChanged
|
||||||
void
|
void
|
||||||
Shape::ObjectChanged(const Observable* object)
|
Shape::ObjectChanged(const Observable* object)
|
||||||
@@ -219,7 +231,9 @@ Shape::Bounds(bool updateLast) const
|
|||||||
double left, top, right, bottom;
|
double left, top, right, bottom;
|
||||||
|
|
||||||
::VertexSource& source = const_cast<Shape*>(this)->VertexSource();
|
::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);
|
&left, &top, &right, &bottom);
|
||||||
|
|
||||||
BRect bounds(left, top, right, bottom);
|
BRect bounds(left, top, right, bottom);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PathContainer.h"
|
#include "PathContainer.h"
|
||||||
#include "PathSource.h"
|
#include "PathSource.h"
|
||||||
|
#include "Transformable.h"
|
||||||
#include "VectorPath.h"
|
#include "VectorPath.h"
|
||||||
|
|
||||||
class Style;
|
class Style;
|
||||||
@@ -28,6 +29,7 @@ class ShapeListener {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Shape : public IconObject,
|
class Shape : public IconObject,
|
||||||
|
public Transformable,
|
||||||
public Observer, // observing all the paths and the style
|
public Observer, // observing all the paths and the style
|
||||||
public PathContainerListener,
|
public PathContainerListener,
|
||||||
public PathListener {
|
public PathListener {
|
||||||
@@ -36,6 +38,9 @@ class Shape : public IconObject,
|
|||||||
Shape(const Shape& other);
|
Shape(const Shape& other);
|
||||||
virtual ~Shape();
|
virtual ~Shape();
|
||||||
|
|
||||||
|
// Transformable interface
|
||||||
|
virtual void TransformationChanged();
|
||||||
|
|
||||||
// Observer interface
|
// Observer interface
|
||||||
virtual void ObjectChanged(const Observable* object);
|
virtual void ObjectChanged(const Observable* object);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
@@ -0,0 +1,594 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
||||||
@@ -0,0 +1,615 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
Reference in New Issue
Block a user