Reference image: add a transform origin

This is stored as an offset from the image position,
which is the centre of the image.  When interactively edited, the
transform origin stays in place. This makes it easier to scale an image
around some fixed feature in the image.

For now this is not saved out to the format, so it
reinitialises to (0, 0) each time. This means scale around
the midpoint, which is the current behaviour.
This commit is contained in:
John Beard 2024-09-26 20:21:20 +01:00
parent 215533f31a
commit ebfe9df8f9
3 changed files with 135 additions and 36 deletions

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2011 jean-pierre.charras
* Copyright (C) 2022 Mike Williams
* Copyright (C) 2011-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2011-2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -23,6 +23,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "pcb_reference_image.h"
#include <pcb_draw_panel_gal.h>
#include <plotters/plotter.h>
#include <settings/color_settings.h>
@ -33,7 +35,6 @@
#include <eda_draw_frame.h>
#include <core/mirror.h>
#include <board.h>
#include <pcb_reference_image.h>
#include <trigo.h>
#include <geometry/shape_rect.h>
@ -43,26 +44,31 @@ using KIGFX::PCB_PAINTER;
using KIGFX::PCB_RENDER_SETTINGS;
PCB_REFERENCE_IMAGE::PCB_REFERENCE_IMAGE( BOARD_ITEM* aParent, const VECTOR2I& pos,
PCB_REFERENCE_IMAGE::PCB_REFERENCE_IMAGE( BOARD_ITEM* aParent, const VECTOR2I& aPos,
PCB_LAYER_ID aLayer ) :
BOARD_ITEM( aParent, PCB_REFERENCE_IMAGE_T, aLayer )
BOARD_ITEM( aParent, PCB_REFERENCE_IMAGE_T, aLayer ), m_pos( aPos ),
m_transformOriginOffset( 0, 0 )
{
m_pos = pos;
m_bitmapBase = new BITMAP_BASE();
m_bitmapBase->SetPixelSizeIu( (float) pcbIUScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI() );
}
PCB_REFERENCE_IMAGE::PCB_REFERENCE_IMAGE( const PCB_REFERENCE_IMAGE& aPCBBitmap ) :
BOARD_ITEM( aPCBBitmap )
BOARD_ITEM( aPCBBitmap ), m_pos( aPCBBitmap.m_pos ),
m_transformOriginOffset( aPCBBitmap.m_transformOriginOffset )
{
m_pos = aPCBBitmap.m_pos;
m_bitmapBase = new BITMAP_BASE( *aPCBBitmap.m_bitmapBase );
m_bitmapBase->SetPixelSizeIu( (float) pcbIUScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI() );
}
PCB_REFERENCE_IMAGE::~PCB_REFERENCE_IMAGE()
{
delete m_bitmapBase;
}
PCB_REFERENCE_IMAGE& PCB_REFERENCE_IMAGE::operator=( const BOARD_ITEM& aItem )
{
wxCHECK_MSG( Type() == aItem.Type(), *this,
@ -352,6 +358,20 @@ static struct PCB_REFERENCE_IMAGE_DESC
&PCB_REFERENCE_IMAGE::GetImageScale ),
groupImage );
propMgr.AddProperty( new PROPERTY<PCB_REFERENCE_IMAGE, int>(
_HKI( "Transform Offset X" ),
&PCB_REFERENCE_IMAGE::SetTransformOriginOffsetX,
&PCB_REFERENCE_IMAGE::GetTransformOriginOffsetX,
PROPERTY_DISPLAY::PT_COORD, ORIGIN_TRANSFORMS::ABS_X_COORD ),
groupImage );
propMgr.AddProperty( new PROPERTY<PCB_REFERENCE_IMAGE, int>(
_HKI( "Transform Offset Y" ),
&PCB_REFERENCE_IMAGE::SetTransformOriginOffsetY,
&PCB_REFERENCE_IMAGE::GetTransformOriginOffsetY,
PROPERTY_DISPLAY::PT_COORD, ORIGIN_TRANSFORMS::ABS_Y_COORD ),
groupImage );
// For future use
const wxString greyscale = _HKI( "Greyscale" );
}

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2011 jean-pierre.charras
* Copyright (C) 2022 Mike Williams
* Copyright (C) 2011-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2011-2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -23,9 +23,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef PCB_REFERENCE_IMAGE_H
#define PCB_REFERENCE_IMAGE_H
#pragma once
#include <board_item.h>
#include <bitmap_base.h>
@ -42,7 +40,7 @@ public:
PCB_REFERENCE_IMAGE( const PCB_REFERENCE_IMAGE& aPcbBitmap );
~PCB_REFERENCE_IMAGE() { delete m_bitmapBase; }
~PCB_REFERENCE_IMAGE();
PCB_REFERENCE_IMAGE& operator=( const BOARD_ITEM& aItem );
@ -132,6 +130,12 @@ public:
VECTOR2I GetPosition() const override { return m_pos; }
void SetPosition( const VECTOR2I& aPosition ) override { m_pos = aPosition; }
/**
* Get the center of scaling, etc, relative to the image center (GetPosition()).
*/
VECTOR2I GetTransformOriginOffset() const { return m_transformOriginOffset; }
void SetTransformOriginOffset( const VECTOR2I& aCenter ) { m_transformOriginOffset = aCenter; }
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
@ -146,12 +150,18 @@ public:
void Show( int nestLevel, std::ostream& os ) const override;
#endif
// Property manager interfaces
int GetTransformOriginOffsetX() const { return m_transformOriginOffset.x; }
void SetTransformOriginOffsetX( int aX ) { m_transformOriginOffset.x = aX; }
int GetTransformOriginOffsetY() const { return m_transformOriginOffset.y; }
void SetTransformOriginOffsetY( int aY ) { m_transformOriginOffset.y = aY; }
protected:
void swapData( BOARD_ITEM* aItem ) override;
private:
VECTOR2I m_pos; // XY coordinates of center of the bitmap
VECTOR2I m_pos; // XY coordinates of center of the bitmap
///< Center of scaling, etc, relative to the image center
VECTOR2I m_transformOriginOffset;
BITMAP_BASE* m_bitmapBase; // the BITMAP_BASE item
};
#endif // PCB_REFERENCE_IMAGE_H

View File

@ -77,6 +77,12 @@ enum RECT_LINES
};
enum REFIMG_POINTS
{
REFIMG_ORIGIN = RECT_BOT_LEFT + 1
};
enum TABLECELL_POINTS
{
COL_WIDTH, ROW_HEIGHT
@ -207,15 +213,17 @@ std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
{
case PCB_REFERENCE_IMAGE_T:
{
PCB_REFERENCE_IMAGE* bitmap = (PCB_REFERENCE_IMAGE*) aItem;
VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
const PCB_REFERENCE_IMAGE* refImage = static_cast<const PCB_REFERENCE_IMAGE*>( aItem );
const VECTOR2I topLeft = refImage->GetPosition() - refImage->GetSize() / 2;
const VECTOR2I botRight = refImage->GetPosition() + refImage->GetSize() / 2;
points->AddPoint( topLeft );
points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
points->AddPoint( botRight );
points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
points->AddPoint( refImage->GetPosition() + refImage->GetTransformOriginOffset() );
break;
}
@ -1352,24 +1360,82 @@ void PCB_POINT_EDITOR::updateItem( BOARD_COMMIT* aCommit )
{
case PCB_REFERENCE_IMAGE_T:
{
PCB_REFERENCE_IMAGE* bitmap = (PCB_REFERENCE_IMAGE*) item;
VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
PCB_REFERENCE_IMAGE* bitmap = static_cast<PCB_REFERENCE_IMAGE*>( item );
const VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
const VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
const VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
const VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
const VECTOR2I xfrmOrigin = m_editPoints->Point( REFIMG_ORIGIN ).GetPosition();
pinEditedCorner( topLeft, topRight, botLeft, botRight );
if( isModified( m_editPoints->Point( REFIMG_ORIGIN ) ) )
{
// Moving the transform origin
// As the other points didn't move, we can get the image extent from them
const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
bitmap->SetTransformOriginOffset( newOffset );
}
else
{
const VECTOR2I oldOrigin = bitmap->GetPosition() + bitmap->GetTransformOriginOffset();
const VECTOR2I oldSize = bitmap->GetSize();
double oldWidth = bitmap->GetSize().x;
double newWidth = std::max( topRight.x - topLeft.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
double widthRatio = newWidth / oldWidth;
OPT_VECTOR2I newCorner;
OPT_VECTOR2I oldCorner;
double oldHeight = bitmap->GetSize().y;
double newHeight =
std::max( botLeft.y - topLeft.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
double heightRatio = newHeight / oldHeight;
if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
{
newCorner = topLeft;
oldCorner = ( bitmap->GetPosition() - oldSize / 2 );
}
else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
{
newCorner = topRight;
oldCorner = ( bitmap->GetPosition() - VECTOR2I( -oldSize.x, oldSize.y ) / 2 );
}
else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
{
newCorner = botLeft;
oldCorner = ( bitmap->GetPosition() - VECTOR2I( oldSize.x, -oldSize.y ) / 2 );
}
else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
{
newCorner = botRight;
oldCorner = ( bitmap->GetPosition() + oldSize / 2 );
}
bitmap->SetImageScale( bitmap->GetImageScale() * std::min( widthRatio, heightRatio ) );
if( newCorner && oldCorner )
{
// Turn in the respective vectors from the origin
*newCorner -= xfrmOrigin;
*oldCorner -= oldOrigin;
// If we tried to cross the origin, clamp it to stop it
if( sign( newCorner->x ) != sign( oldCorner->x )
|| sign( newCorner->y ) != sign( oldCorner->y ) )
{
*newCorner = VECTOR2I( 0, 0 );
}
const double newLength = newCorner->EuclideanNorm();
const double oldLength = oldCorner->EuclideanNorm();
double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
// Clamp the scaling to a minimum of 50 mils
VECTOR2I newSize = oldSize * ratio;
double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
// What we need to do here is to scale the image by the ratio, but then
// also move the image so that the transform origin is in the same place
bitmap->SetImageScale( bitmap->GetImageScale() * ratio );
const VECTOR2I newOffset = bitmap->GetTransformOriginOffset() * ratio;
bitmap->SetTransformOriginOffset( newOffset );
bitmap->SetPosition( xfrmOrigin - newOffset );
}
}
break;
}
@ -2002,15 +2068,18 @@ void PCB_POINT_EDITOR::updatePoints()
{
case PCB_REFERENCE_IMAGE_T:
{
PCB_REFERENCE_IMAGE* bitmap = (PCB_REFERENCE_IMAGE*) item;
VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
const PCB_REFERENCE_IMAGE* bitmap = static_cast<const PCB_REFERENCE_IMAGE*>( item );
const VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
const VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
m_editPoints->Point( REFIMG_ORIGIN )
.SetPosition( bitmap->GetPosition() + bitmap->GetTransformOriginOffset() );
break;
}