Add a ruler tool to pcbnew GAL

This allows to measure between features on a PCB. It uses a preview
EDA_ITEM in common, but due to the use of the IDs, it's currently
Pcbnew/Modedit only.

This also adds several "utils" for graphical functons useful when
drawing preview items on GAL canvases.

Fixes: lp:1467313
* https://bugs.launchpad.net/kicad/+bug/1467313
This commit is contained in:
John Beard 2017-03-09 17:09:13 +08:00 committed by Maciej Suminski
parent 9c08873210
commit 8c3b8ee693
22 changed files with 934 additions and 4 deletions

View File

@ -339,6 +339,7 @@ set( BMAPS_MID
load_module_lib load_module_lib
local_ratsnest local_ratsnest
locked locked
measurement
mirepcb mirepcb
mirror_h mirror_h
mirror_v mirror_v

View File

@ -0,0 +1,24 @@
/* Do not modify this file, it was automatically generated by the
* PNG2cpp CMake script, using a *.png file as input.
*/
#include <bitmaps.h>
static const unsigned char png[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x04, 0x00, 0x00, 0x00, 0x03, 0x43, 0x84,
0x45, 0x00, 0x00, 0x00, 0x76, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0x2a, 0x20,
0x35, 0x01, 0x3b, 0x1b, 0x2f, 0x48, 0xfb, 0x92, 0xaa, 0x0d, 0xd5, 0xa2, 0x9d, 0xf6, 0x85, 0x58,
0x4d, 0xff, 0xd3, 0xae, 0xc6, 0x72, 0x33, 0x30, 0xc4, 0x72, 0xa7, 0x5d, 0x4d, 0xfb, 0x4f, 0xbc,
0xa6, 0xff, 0xe9, 0xf3, 0x19, 0x18, 0xd2, 0xe7, 0x83, 0x58, 0x24, 0x68, 0x02, 0x29, 0x86, 0xd1,
0x78, 0x81, 0xdd, 0x25, 0xe3, 0xff, 0xa4, 0x41, 0xa0, 0x26, 0xe3, 0xff, 0x17, 0x49, 0x84, 0x50,
0x4d, 0xc8, 0x1a, 0x09, 0xb3, 0x51, 0x34, 0x11, 0x4b, 0x92, 0xab, 0xc9, 0xf6, 0x1a, 0x19, 0x01,
0x81, 0x3d, 0x90, 0x09, 0x06, 0xfc, 0x70, 0xd4, 0x94, 0x5e, 0x8e, 0x43, 0x53, 0x3d, 0xde, 0xf4,
0x07, 0xd2, 0x86, 0xa1, 0xa9, 0x9e, 0x61, 0x14, 0xe0, 0x07, 0x00, 0x55, 0x88, 0x33, 0x29, 0x9a,
0xe8, 0x0b, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
};
const BITMAP_OPAQUE measurement_xpm[1] = {{ png, sizeof( png ), "measurement_xpm" }};
//EOF

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="26"
width="26"
version="1.1"
id="svg2"
inkscape:version="0.92.1 r"
sodipodi:docname="measurement.svg">
<metadata
id="metadata50">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1146"
inkscape:window-height="964"
id="namedview48"
showgrid="false"
inkscape:zoom="1"
inkscape:cx="18.568355"
inkscape:cy="20.983557"
inkscape:window-x="768"
inkscape:window-y="96"
inkscape:window-maximized="0"
inkscape:current-layer="svg2"
inkscape:snap-to-guides="false"
inkscape:snap-grids="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:measure-start="5,19"
inkscape:measure-end="14,12">
<inkscape:grid
type="xygrid"
id="grid3006"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<defs
id="defs4" />
<path
style="fill:#666666;fill-rule:evenodd"
d="m 2,10 v 9 l 2,2 H 5 V 10 H 4"
id="path9597"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path9599"
d="m 15,10 v 9 l -2,2 H 12 V 10 h 1"
style="fill:#666666;fill-rule:evenodd" />
<path
style="fill:#666666;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 8,9 H 5 V 3 l 3,4 z"
id="path9621"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path9623"
d="m 9,9 h 3 V 3 L 9,7 Z"
style="fill:#666666;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:1;fill:#d1d1d1;fill-opacity:1;stroke:none;stroke-width:1"
d="M 1.4999999,8.5 H 26 v 5 H 1.4999999 Z"
id="rect9595"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 21.5,10 v 3"
id="path9601"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path9603"
d="m 18.5,11 v 2"
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path9605"
d="m 15.5,10 v 3"
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 12.5,11 v 2"
id="path9607"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path9609"
d="m 9.5,10 v 3"
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 6.5,11 v 2"
id="path9611"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.5,10 v 3"
id="path9613"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#333333;stroke-width:1"
d="M 26,13.5 H 1.7613635 c -0.1447954,0 -0.2613636,-0.1115 -0.2613636,-0.25 v -4.5 c 0,-0.1385 0.1165682,-0.25 0.2613636,-0.25 H 26"
id="rect9617"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssssc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 24.5,11 v 2"
id="path9625"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -184,6 +184,8 @@ set( COMMON_PAGE_LAYOUT_SRCS
) )
set( COMMON_PREVIEW_ITEMS_SRCS set( COMMON_PREVIEW_ITEMS_SRCS
preview_items/preview_utils.cpp
preview_items/ruler_item.cpp
preview_items/simple_overlay_item.cpp preview_items/simple_overlay_item.cpp
preview_items/selection_area.cpp preview_items/selection_area.cpp
) )

View File

@ -0,0 +1,177 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2017 Kicad Developers, see change_log.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <preview_items/preview_utils.h>
#include <gal/graphics_abstraction_layer.h>
#include <base_units.h>
using namespace KIGFX;
COLOR4D KIGFX::PREVIEW::PreviewOverlayDefaultColor()
{
return COLOR4D( 1.0, 1.0, 1.0, 1.0 );
}
double KIGFX::PREVIEW::PreviewOverlayFillAlpha()
{
return 0.2;
}
double KIGFX::PREVIEW::PreviewOverlayDeemphAlpha( bool aDeemph )
{
return aDeemph ? 0.5 : 1.0;
}
COLOR4D KIGFX::PREVIEW::PreviewOverlaySpecialAngleColor()
{
return COLOR4D( 0.5, 1.0, 0.5, 1.0 );
}
static wxString getDimensionUnit( EDA_UNITS_T aUnits )
{
switch( aUnits )
{
case INCHES:
return _( "\"" );
case MILLIMETRES:
return _( "mm" );
case DEGREES:
return _( "°" );
case UNSCALED_UNITS:
break;
// no default: handle all cases
}
return wxEmptyString;
}
static wxString formatPreviewDimension( double aVal, EDA_UNITS_T aUnits )
{
int precision = 4;
// show a sane precision for the preview, which doesn't need to
// be accurate down to the nanometre
switch( aUnits )
{
case MILLIMETRES:
precision = 2; // 10um
break;
case INCHES:
precision = 4; // 1mil
break;
case DEGREES:
precision = 1; // 0.1deg (limit of formats anyway)
break;
case UNSCALED_UNITS:
break;
}
const wxString fmtStr = wxString::Format( "%%.%df", precision );
wxString str = wxString::Format( fmtStr, To_User_Unit( aUnits, aVal ) );
const wxString symbol = getDimensionUnit( aUnits );
if( symbol.size() )
str << " " << symbol;
return str;
}
wxString KIGFX::PREVIEW::DimensionLabel( const wxString& prefix,
double aVal, EDA_UNITS_T aUnits )
{
wxString str;
if( prefix.size() )
str << prefix << ": ";
str << formatPreviewDimension( aVal, aUnits );
return str;
}
void KIGFX::PREVIEW::SetConstantGlyphHeight( KIGFX::GAL& aGal, double aHeight )
{
aHeight /= aGal.GetWorldScale();
auto glyphSize = aGal.GetGlyphSize();
glyphSize = glyphSize * ( aHeight / glyphSize.y );
aGal.SetGlyphSize( glyphSize );
}
void KIGFX::PREVIEW::DrawTextNextToCursor( KIGFX::GAL& aGal,
const VECTOR2D& aCursorPos, const VECTOR2D& aTextQuadrant,
const std::vector<wxString>& aStrings )
{
auto glyphSize = aGal.GetGlyphSize();
const auto lineSpace = glyphSize.y * 0.2;
auto linePitch = glyphSize.y + lineSpace;
// radius string goes on the right of the cursor centre line
// with a small horizontal offset (enough to keep clear of a
// system cursor if present)
auto textPos = aCursorPos;
// if the text goes above the cursor, shift it up
if( aTextQuadrant.y > 0 )
{
textPos.y -= linePitch * ( aStrings.size() + 1 );
}
if( aTextQuadrant.x < 0 )
{
aGal.SetHorizontalJustify( GR_TEXT_HJUSTIFY_LEFT );
textPos.x += 15.0 / aGal.GetWorldScale();
}
else
{
aGal.SetHorizontalJustify( GR_TEXT_HJUSTIFY_RIGHT );
textPos.x -= 15.0 / aGal.GetWorldScale();
}
aGal.SetStrokeColor( PreviewOverlayDefaultColor().WithAlpha(
PreviewOverlayDeemphAlpha( true ) ) );
aGal.SetIsFill( false );
// write strings top-to-bottom
for( const auto& str : aStrings )
{
textPos.y += linePitch;
aGal.BitmapText( str, textPos, 0.0 );
}
}

View File

@ -0,0 +1,261 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 Kicad Developers, see change_log.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <preview_items/ruler_item.h>
#include <preview_items/preview_utils.h>
#include <gal/graphics_abstraction_layer.h>
#include <layers_id_colors_and_visibility.h>
#include <view/view.h>
#include <base_units.h>
#include <common.h>
using namespace KIGFX::PREVIEW;
static const double maxTickDensity = 10.0; // min pixels between tick marks
static const double midTickLengthFactor = 1.5;
static const double majorTickLengthFactor = 2.5;
static void drawCursorStrings( KIGFX::GAL& aGal, const VECTOR2D& aCursor,
const VECTOR2D& aRulerVec )
{
// draw the cursor labels
std::vector<wxString> cursorStrings;
cursorStrings.push_back( DimensionLabel( "r", aRulerVec.EuclideanNorm(), g_UserUnit ) );
double degs = RAD2DECIDEG( -aRulerVec.Angle() );
cursorStrings.push_back( DimensionLabel( "θ", degs, DEGREES ) );
for( auto& str: cursorStrings )
{
// FIXME: remove spaces that choke OpenGL lp:1668455
str.erase( std::remove( str.begin(), str.end(), ' ' ), str.end() );
}
auto temp = aRulerVec;
DrawTextNextToCursor( aGal, aCursor, -temp, cursorStrings );
}
/**
* Description of a "tick format" for a scale factor - how many ticks there are
* between medium/major ticks and how each scale relates to the last one
*/
struct TICK_FORMAT
{
double divisionBase; ///> multiple from the last scale
int majorStep; ///> ticks between major ticks
int midStep; ///> ticks between medium ticks (0 if no medium ticks)
};
static TICK_FORMAT getTickFormatForScale( double aScale, double& aTickSpace )
{
// simple 1/2/5 scales per decade
static std::vector<TICK_FORMAT> tickFormats =
{
{ 2, 10, 5 }, // |....:....|
{ 2, 5, 0 }, // |....|
{ 2.5, 2, 0 }, // |.|.|
};
// could start at a set number of MM, but that's not available in common
aTickSpace = 1;
// convert to a round (mod-10) number of mils
if( g_UserUnit == INCHES )
{
aTickSpace *= 2.54;
}
int tickFormat = 0;
while( true )
{
const auto pixelSpace = aTickSpace * aScale;
if( pixelSpace >= maxTickDensity )
break;
tickFormat = ( tickFormat + 1 ) % tickFormats.size();
aTickSpace *= tickFormats[tickFormat].divisionBase;
}
return tickFormats[tickFormat];
}
/**
* Draw labelled ticks on a line. Ticks are spaced according to a
* maximum density. Miror ticks are not labelled.
*
* @param aGal the GAL to draw on
* @param aOrigin start of line to draw ticks on
* @param aLine line vector
* @param aMinorTickLen length of minor ticks in IU
*/
void drawTicksAlongLine( KIGFX::GAL& aGal, const VECTOR2D& aOrigin,
const VECTOR2D& aLine, double aMinorTickLen )
{
VECTOR2D tickLine = aLine.Rotate( -M_PI_2 );
double tickSpace;
TICK_FORMAT tickF = getTickFormatForScale( aGal.GetWorldScale(), tickSpace );
// number of ticks in whole ruler
int numTicks = (int) std::ceil( aLine.EuclideanNorm() / tickSpace );
// work out which way up the tick labels go
double labelAngle = -tickLine.Angle();
if( aLine.Angle() > 0 )
{
aGal.SetHorizontalJustify( GR_TEXT_HJUSTIFY_LEFT );
}
else
{
aGal.SetHorizontalJustify( GR_TEXT_HJUSTIFY_RIGHT );
labelAngle += M_PI;
}
// text and ticks are dimmed
aGal.SetStrokeColor( PreviewOverlayDefaultColor().WithAlpha( PreviewOverlayDeemphAlpha( true ) ) );
const auto labelOffset = tickLine.Resize( aMinorTickLen * ( majorTickLengthFactor + 1 ) );
for( int i = 0; i < numTicks; ++i )
{
const auto tickPos = aOrigin + aLine.Resize( tickSpace * i );
double length = aMinorTickLen;
bool drawLabel = false;
if( i % tickF.majorStep == 0)
{
drawLabel = true;
length *= majorTickLengthFactor;
}
else if( tickF.midStep && i % tickF.midStep == 0 )
{
drawLabel = true;
length *= midTickLengthFactor;
}
aGal.DrawLine( tickPos, tickPos + tickLine.Resize( length ) );
if( drawLabel )
{
wxString label = DimensionLabel( "", tickSpace * i, g_UserUnit );
// FIXME: spaces choke OpenGL lp:1668455
label.erase( std::remove( label.begin(), label.end(), ' ' ), label.end() );
aGal.BitmapText( label, tickPos + labelOffset, labelAngle );
}
}
}
/**
* Draw simple ticks on the back of a line such that the line is
* divided into n parts.
*
* @param aGal the GAL to draw on
* @param aOrigin start of line to draw ticks on
* @param aLine line vector
* @param aTickLen length of ticks in IU
* @param aNumDivisions number of parts to divide the line into
*/
void drawBacksideTicks( KIGFX::GAL& aGal, const VECTOR2D& aOrigin,
const VECTOR2D& aLine, double aTickLen, int aNumDivisions )
{
const double backTickSpace = aLine.EuclideanNorm() / aNumDivisions;
const auto backTickVec = aLine.Rotate( M_PI_2 ).Resize( aTickLen );
for( int i = 0; i < aNumDivisions + 1; ++i )
{
const auto backTickPos = aOrigin + aLine.Resize( backTickSpace * i );
aGal.DrawLine( backTickPos, backTickPos + backTickVec );
}
}
RULER_ITEM::RULER_ITEM():
EDA_ITEM( NOT_USED ) // Never added to anything - just a preview
{}
const BOX2I RULER_ITEM::ViewBBox() const
{
BOX2I tmp;
tmp.SetOrigin( m_origin );
tmp.SetEnd( m_end );
tmp.Normalize();
return tmp;
}
void RULER_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
{
aLayers[0] = ITEM_GAL_LAYER( GP_OVERLAY );
aCount = 1;
}
void RULER_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
{
auto& gal = *aView->GetGAL();
gal.SetLineWidth( 1.0 );
gal.SetIsStroke( true );
gal.SetIsFill( false );
gal.SetStrokeColor( PreviewOverlayDefaultColor() );
// draw the main line from the origin to cursor
gal.DrawLine( m_origin, m_end );
VECTOR2D rulerVec( m_end - m_origin );
// constant text size on screen
SetConstantGlyphHeight( gal, 12.0 );
drawCursorStrings( gal, m_end, rulerVec );
// tick label size
SetConstantGlyphHeight( gal, 10.0 );
// basic tick size
const double minorTickLen = 5.0 / gal.GetWorldScale();
drawTicksAlongLine( gal, m_origin, rulerVec, minorTickLen );
gal.SetStrokeColor( PreviewOverlayDefaultColor().WithAlpha( PreviewOverlayDeemphAlpha( true ) ) );
drawBacksideTicks( gal, m_origin, rulerVec, minorTickLen * majorTickLengthFactor, 2 );
// draw the back of the origin "crosshair"
gal.DrawLine( m_origin, m_origin + rulerVec.Resize( -minorTickLen * midTickLengthFactor ) );
}

View File

@ -283,6 +283,7 @@ EXTERN_BITMAP( load_module_board_xpm )
EXTERN_BITMAP( load_module_lib_xpm ) EXTERN_BITMAP( load_module_lib_xpm )
EXTERN_BITMAP( local_ratsnest_xpm ) EXTERN_BITMAP( local_ratsnest_xpm )
EXTERN_BITMAP( locked_xpm ) EXTERN_BITMAP( locked_xpm )
EXTERN_BITMAP( measurement_xpm )
EXTERN_BITMAP( mirepcb_xpm ) EXTERN_BITMAP( mirepcb_xpm )
EXTERN_BITMAP( mirror_h_xpm ) EXTERN_BITMAP( mirror_h_xpm )
EXTERN_BITMAP( mirror_v_xpm ) EXTERN_BITMAP( mirror_v_xpm )

View File

@ -0,0 +1,90 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2017 Kicad Developers, see change_log.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef PREVIEW_PREVIEW_UTILS__H_
#define PREVIEW_PREVIEW_UTILS__H_
#include <common.h>
#include <gal/color4d.h>
#include <math/vector2d.h>
namespace KIGFX
{
class GAL;
namespace PREVIEW
{
/**
* The default fill/stroke color of preview overlay items
*/
COLOR4D PreviewOverlayDefaultColor();
/**
* The default alpha of overlay fills
*/
double PreviewOverlayFillAlpha();
/**
* Default alpha of "de-emphasised" features (like previously locked-in
* lines
*/
double PreviewOverlayDeemphAlpha( bool aDeemph = true );
/**
* The colour of "special" angle overlay features
*/
COLOR4D PreviewOverlaySpecialAngleColor();
/**
* Get a formatted string showing a dimension to a sane precision
* with an optional prefix and unit suffix.
*/
wxString DimensionLabel( const wxString& prefix, double aVal,
EDA_UNITS_T aUnits );
/**
* Set the GAL glyph height to a constant scaled value, so that it
* always looks the same on screen
*
* @param aHeight the height of the glyph, in pixels
*/
void SetConstantGlyphHeight( KIGFX::GAL& aGal, double aHeight );
/**
* Draw strings next to the cursor
*
* @param aGal the GAL to draw on
* @param aCursorPos the position of the cursor to draw next to
* @param aTextQuadrant a vector pointing to the quadrant to draw the
* text in
* @param aStrings list of strings to draw, top to bottom
*/
void DrawTextNextToCursor( KIGFX::GAL& aGal,
const VECTOR2D& aCursorPos, const VECTOR2D& aTextQuadrant,
const std::vector<wxString>& aStrings );
} // PREVIEW
} // KIGFX
#endif // PREVIEW_PREVIEW_UTILS__H_

View File

@ -0,0 +1,95 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef PREVIEW_ITEMS_RULER_ITEM_H
#define PREVIEW_ITEMS_RULER_ITEM_H
#include <base_struct.h>
namespace KIGFX
{
class GAL;
namespace PREVIEW
{
/**
* Class RULER_ITEM
*
* A drawn ruler item for showing the distance between two points.
*/
class RULER_ITEM : public EDA_ITEM
{
public:
RULER_ITEM();
///> @copydoc EDA_ITEM::ViewBBox()
const BOX2I ViewBBox() const override;
///> @copydoc EDA_ITEM::ViewGetLayers()
void ViewGetLayers( int aLayers[], int& aCount ) const override;
///> @copydoc EDA_ITEM::ViewDraw();
void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override final;
#if defined(DEBUG)
void Show( int x, std::ostream& st ) const override
{
}
#endif
/**
* Get class name
* @return string "RULER_ITEM"
*/
wxString GetClass() const override
{
return wxT( "RULER_ITEM" );
}
///> Set the origin of the ruler (the fixed end)
void SetOrigin( VECTOR2I aOrigin )
{
m_origin = aOrigin;
}
/**
* Set the current end of the rectangle (the end that moves
* with the cursor.
*/
void SetEnd( VECTOR2I aEnd )
{
m_end = aEnd;
}
private:
VECTOR2I m_origin, m_end;
};
} // PREVIEW
} // KIGFX
#endif // PREVIEW_ITEMS_RULER_ITEM_H

View File

@ -1525,6 +1525,11 @@ void PCB_EDIT_FRAME::OnSelectTool( wxCommandEvent& aEvent )
Compile_Ratsnest( &dc, true ); Compile_Ratsnest( &dc, true );
break; break;
// collect GAL-only tools here
case ID_PCB_MEASUREMENT_TOOL:
SetToolID( id, wxCURSOR_DEFAULT, _( "Unsupported tool in this canvas" ) );
break;
} }
} }

View File

@ -962,6 +962,11 @@ void FOOTPRINT_EDIT_FRAME::OnVerticalToolbar( wxCommandEvent& aEvent )
SetToolID( id, wxCURSOR_BULLSEYE, _( "Delete item" ) ); SetToolID( id, wxCURSOR_BULLSEYE, _( "Delete item" ) );
break; break;
case ID_MODEDIT_MEASUREMENT_TOOL:
DisplayError( this, wxT( "Unsupported tool in legacy canvas" ) );
SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
break;
default: default:
wxFAIL_MSG( wxT( "Unknown command id." ) ); wxFAIL_MSG( wxT( "Unknown command id." ) );
SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString ); SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );

View File

@ -200,6 +200,11 @@ void FOOTPRINT_EDIT_FRAME::OnLeftClick( wxDC* DC, const wxPoint& MousePos )
break; break;
case ID_MODEDIT_MEASUREMENT_TOOL:
DisplayError( this, wxT( "Unsupported tool in legacy canvas" ) );
SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
break;
default: default:
DisplayError( this, wxT( "FOOTPRINT_EDIT_FRAME::ProcessCommand error" ) ); DisplayError( this, wxT( "FOOTPRINT_EDIT_FRAME::ProcessCommand error" ) );
SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString ); SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );

View File

@ -116,7 +116,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_EDIT_FRAME, PCB_BASE_FRAME )
// Vertical tool bar button click event handler. // Vertical tool bar button click event handler.
EVT_TOOL( ID_NO_TOOL_SELECTED, FOOTPRINT_EDIT_FRAME::OnVerticalToolbar ) EVT_TOOL( ID_NO_TOOL_SELECTED, FOOTPRINT_EDIT_FRAME::OnVerticalToolbar )
EVT_TOOL( ID_ZOOM_SELECTION, FOOTPRINT_EDIT_FRAME::OnVerticalToolbar ) EVT_TOOL( ID_ZOOM_SELECTION, FOOTPRINT_EDIT_FRAME::OnVerticalToolbar )
EVT_TOOL_RANGE( ID_MODEDIT_PAD_TOOL, ID_MODEDIT_PLACE_GRID_COORD, EVT_TOOL_RANGE( ID_MODEDIT_PAD_TOOL, ID_MODEDIT_MEASUREMENT_TOOL,
FOOTPRINT_EDIT_FRAME::OnVerticalToolbar ) FOOTPRINT_EDIT_FRAME::OnVerticalToolbar )
// Options Toolbar (ID_TB_OPTIONS_SHOW_PADS_SKETCH id is managed in PCB_BASE_FRAME) // Options Toolbar (ID_TB_OPTIONS_SHOW_PADS_SKETCH id is managed in PCB_BASE_FRAME)
@ -195,7 +195,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_EDIT_FRAME, PCB_BASE_FRAME )
EVT_UPDATE_UI( ID_NO_TOOL_SELECTED, FOOTPRINT_EDIT_FRAME::OnUpdateVerticalToolbar ) EVT_UPDATE_UI( ID_NO_TOOL_SELECTED, FOOTPRINT_EDIT_FRAME::OnUpdateVerticalToolbar )
EVT_UPDATE_UI( ID_ZOOM_SELECTION, FOOTPRINT_EDIT_FRAME::OnUpdateVerticalToolbar ) EVT_UPDATE_UI( ID_ZOOM_SELECTION, FOOTPRINT_EDIT_FRAME::OnUpdateVerticalToolbar )
EVT_UPDATE_UI_RANGE( ID_MODEDIT_PAD_TOOL, ID_MODEDIT_PLACE_GRID_COORD, EVT_UPDATE_UI_RANGE( ID_MODEDIT_PAD_TOOL, ID_MODEDIT_MEASUREMENT_TOOL,
FOOTPRINT_EDIT_FRAME::OnUpdateVerticalToolbar ) FOOTPRINT_EDIT_FRAME::OnUpdateVerticalToolbar )
// Option toolbar: // Option toolbar:

View File

@ -439,6 +439,11 @@ void PCB_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition )
m_canvas->DrawGridAxis( aDC, GR_COPY, GetBoard()->GetGridOrigin() ); m_canvas->DrawGridAxis( aDC, GR_COPY, GetBoard()->GetGridOrigin() );
break; break;
case ID_PCB_MEASUREMENT_TOOL:
DisplayError( this, wxT( "This tool is not available in the legacy canvas" ) );
SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );
break;
default: default:
DisplayError( this, wxT( "PCB_EDIT_FRAME::OnLeftClick() id error" ) ); DisplayError( this, wxT( "PCB_EDIT_FRAME::OnLeftClick() id error" ) );
SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString ); SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );

View File

@ -259,7 +259,7 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME )
// Vertical main toolbar: // Vertical main toolbar:
EVT_TOOL( ID_NO_TOOL_SELECTED, PCB_EDIT_FRAME::OnSelectTool ) EVT_TOOL( ID_NO_TOOL_SELECTED, PCB_EDIT_FRAME::OnSelectTool )
EVT_TOOL( ID_ZOOM_SELECTION, PCB_EDIT_FRAME::OnSelectTool ) EVT_TOOL( ID_ZOOM_SELECTION, PCB_EDIT_FRAME::OnSelectTool )
EVT_TOOL_RANGE( ID_PCB_HIGHLIGHT_BUTT, ID_PCB_PLACE_GRID_COORD_BUTT, EVT_TOOL_RANGE( ID_PCB_HIGHLIGHT_BUTT, ID_PCB_MEASUREMENT_TOOL,
PCB_EDIT_FRAME::OnSelectTool ) PCB_EDIT_FRAME::OnSelectTool )
EVT_TOOL_RANGE( ID_PCB_MUWAVE_START_CMD, ID_PCB_MUWAVE_END_CMD, EVT_TOOL_RANGE( ID_PCB_MUWAVE_START_CMD, ID_PCB_MUWAVE_END_CMD,
@ -311,7 +311,7 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME )
PCB_EDIT_FRAME::OnUpdateSelectTrackWidth ) PCB_EDIT_FRAME::OnUpdateSelectTrackWidth )
EVT_UPDATE_UI_RANGE( ID_POPUP_PCB_SELECT_VIASIZE1, ID_POPUP_PCB_SELECT_VIASIZE8, EVT_UPDATE_UI_RANGE( ID_POPUP_PCB_SELECT_VIASIZE1, ID_POPUP_PCB_SELECT_VIASIZE8,
PCB_EDIT_FRAME::OnUpdateSelectViaSize ) PCB_EDIT_FRAME::OnUpdateSelectViaSize )
EVT_UPDATE_UI_RANGE( ID_PCB_HIGHLIGHT_BUTT, ID_PCB_PLACE_GRID_COORD_BUTT, EVT_UPDATE_UI_RANGE( ID_PCB_HIGHLIGHT_BUTT, ID_PCB_MEASUREMENT_TOOL,
PCB_EDIT_FRAME::OnUpdateVerticalToolbar ) PCB_EDIT_FRAME::OnUpdateVerticalToolbar )
EVT_UPDATE_UI_RANGE( ID_TB_OPTIONS_SHOW_ZONES, ID_TB_OPTIONS_SHOW_ZONES_OUTLINES_ONLY, EVT_UPDATE_UI_RANGE( ID_TB_OPTIONS_SHOW_ZONES, ID_TB_OPTIONS_SHOW_ZONES_OUTLINES_ONLY,
PCB_EDIT_FRAME::OnUpdateZoneDisplayStyle ) PCB_EDIT_FRAME::OnUpdateZoneDisplayStyle )

View File

@ -40,6 +40,8 @@ enum pcbnew_ids
ID_PCB_DELETE_ITEM_BUTT, ID_PCB_DELETE_ITEM_BUTT,
ID_PCB_PLACE_OFFSET_COORD_BUTT, ID_PCB_PLACE_OFFSET_COORD_BUTT,
ID_PCB_PLACE_GRID_COORD_BUTT, ID_PCB_PLACE_GRID_COORD_BUTT,
ID_PCB_MEASUREMENT_TOOL,
ID_DIFF_PAIR_BUTT, ID_DIFF_PAIR_BUTT,
ID_TUNE_SINGLE_TRACK_LEN_BUTT, ID_TUNE_SINGLE_TRACK_LEN_BUTT,
ID_TUNE_DIFF_PAIR_LEN_BUTT, ID_TUNE_DIFF_PAIR_LEN_BUTT,
@ -343,6 +345,7 @@ enum pcbnew_ids
ID_MODEDIT_ANCHOR_TOOL, ID_MODEDIT_ANCHOR_TOOL,
ID_MODEDIT_DELETE_TOOL, ID_MODEDIT_DELETE_TOOL,
ID_MODEDIT_PLACE_GRID_COORD, ID_MODEDIT_PLACE_GRID_COORD,
ID_MODEDIT_MEASUREMENT_TOOL,
// ID used in module editor: // ID used in module editor:
ID_POPUP_MODEDIT_GLOBAL_EDIT_EDGE, ID_POPUP_MODEDIT_GLOBAL_EDIT_EDGE,

View File

@ -196,6 +196,11 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
_( "Set the origin point for the grid" ), _( "Set the origin point for the grid" ),
wxITEM_CHECK ); wxITEM_CHECK );
m_drawToolBar->AddTool( ID_MODEDIT_MEASUREMENT_TOOL, wxEmptyString,
KiBitmap( measurement_xpm ),
_( "Measure distance between two points" ),
wxITEM_CHECK );
m_drawToolBar->Realize(); m_drawToolBar->Realize();
} }

View File

@ -484,6 +484,11 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
_( "Set the origin point for the grid" ), _( "Set the origin point for the grid" ),
wxITEM_CHECK ); wxITEM_CHECK );
m_drawToolBar->AddTool( ID_PCB_MEASUREMENT_TOOL, wxEmptyString,
KiBitmap( measurement_xpm ),
_( "Measure distance between two points" ),
wxITEM_CHECK );
m_drawToolBar->Realize(); m_drawToolBar->Realize();
} }

View File

@ -35,6 +35,7 @@
#include <class_draw_panel_gal.h> #include <class_draw_panel_gal.h>
#include <module_editor_frame.h> #include <module_editor_frame.h>
#include <array_creator.h> #include <array_creator.h>
#include <pcbnew_id.h>
#include <tool/tool_manager.h> #include <tool/tool_manager.h>
#include <view/view_controls.h> #include <view/view_controls.h>
@ -62,6 +63,8 @@ using namespace std::placeholders;
#include <tools/tool_event_utils.h> #include <tools/tool_event_utils.h>
#include <preview_items/ruler_item.h>
#include <board_commit.h> #include <board_commit.h>
// Edit tool actions // Edit tool actions
@ -149,6 +152,11 @@ TOOL_ACTION PCB_ACTIONS::editModifiedSelection( "pcbnew.InteractiveEdit.Modified
AS_GLOBAL, 0, AS_GLOBAL, 0,
"", "" ); "", "" );
TOOL_ACTION PCB_ACTIONS::measureTool( "pcbnew.InteractiveEdit.measureTool",
AS_GLOBAL, MD_CTRL + MD_SHIFT + 'M',
_( "Measure tool" ), _( "Interactively measure distance between points" ),
nullptr, AF_ACTIVATE );
EDIT_TOOL::EDIT_TOOL() : EDIT_TOOL::EDIT_TOOL() :
PCB_TOOL( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ), PCB_TOOL( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ),
@ -927,6 +935,95 @@ int EDIT_TOOL::ExchangeFootprints( const TOOL_EVENT& aEvent )
} }
int EDIT_TOOL::MeasureTool( const TOOL_EVENT& aEvent )
{
auto& view = *getView();
auto& controls = *getViewControls();
Activate();
frame()->SetToolID( EditingModules() ? ID_MODEDIT_MEASUREMENT_TOOL
: ID_PCB_MEASUREMENT_TOOL,
wxCURSOR_PENCIL, _( "Measure distance between two points" ) );
KIGFX::PREVIEW::RULER_ITEM ruler;
view.Add( &ruler );
view.SetVisible( &ruler, false );
bool originSet = false;
controls.ShowCursor( true );
controls.SetSnapping( true );
while( auto evt = Wait() )
{
const VECTOR2I cursorPos = controls.GetCursorPosition();
if( evt->IsCancel() || evt->IsActivate() )
{
break;
}
// click or drag starts
else if( !originSet &&
( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
{
if( !evt->IsDrag( BUT_LEFT ) )
{
ruler.SetOrigin( cursorPos );
ruler.SetEnd( cursorPos );
}
controls.CaptureCursor( true );
controls.SetAutoPan( true );
originSet = true;
}
else if( !originSet && evt->IsMotion() )
{
// make sure the origin is set before a drag starts
// otherwise you can miss a step
ruler.SetOrigin( cursorPos );
ruler.SetEnd( cursorPos );
}
// second click or mouse up after drag ends
else if( originSet &&
( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
{
originSet = false;
controls.SetAutoPan( false );
controls.CaptureCursor( false );
view.SetVisible( &ruler, false );
}
// move or drag when origin set updates rules
else if( originSet &&
( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
{
ruler.SetEnd( cursorPos );
view.SetVisible( &ruler, true );
view.Update( &ruler, KIGFX::GEOMETRY );
}
else if( evt->IsClick( BUT_RIGHT ) )
{
GetManager()->PassEvent();
}
}
view.SetVisible( &ruler, false );
view.Remove( &ruler );
frame()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
return 0;
}
void EDIT_TOOL::SetTransitions() void EDIT_TOOL::SetTransitions()
{ {
Go( &EDIT_TOOL::Main, PCB_ACTIONS::editActivate.MakeEvent() ); Go( &EDIT_TOOL::Main, PCB_ACTIONS::editActivate.MakeEvent() );
@ -943,6 +1040,7 @@ void EDIT_TOOL::SetTransitions()
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirror.MakeEvent() ); Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirror.MakeEvent() );
Go( &EDIT_TOOL::editFootprintInFpEditor, PCB_ACTIONS::editFootprintInFpEditor.MakeEvent() ); Go( &EDIT_TOOL::editFootprintInFpEditor, PCB_ACTIONS::editFootprintInFpEditor.MakeEvent() );
Go( &EDIT_TOOL::ExchangeFootprints, PCB_ACTIONS::exchangeFootprints.MakeEvent() ); Go( &EDIT_TOOL::ExchangeFootprints, PCB_ACTIONS::exchangeFootprints.MakeEvent() );
Go( &EDIT_TOOL::MeasureTool, PCB_ACTIONS::measureTool.MakeEvent() );
} }

View File

@ -124,6 +124,9 @@ public:
*/ */
int ExchangeFootprints( const TOOL_EVENT& aEvent ); int ExchangeFootprints( const TOOL_EVENT& aEvent );
///> Launches a tool to measure between points
int MeasureTool( const TOOL_EVENT& aEvent );
///> Sets up handlers for various events. ///> Sets up handlers for various events.
void SetTransitions() override; void SetTransitions() override;

View File

@ -151,6 +151,10 @@ boost::optional<TOOL_EVENT> PCB_ACTIONS::TranslateLegacyId( int aId )
case ID_PCB_PLACE_OFFSET_COORD_BUTT: case ID_PCB_PLACE_OFFSET_COORD_BUTT:
return PCB_ACTIONS::drillOrigin.MakeEvent(); return PCB_ACTIONS::drillOrigin.MakeEvent();
case ID_PCB_MEASUREMENT_TOOL:
case ID_MODEDIT_MEASUREMENT_TOOL:
return PCB_ACTIONS::measureTool.MakeEvent();
case ID_PCB_HIGHLIGHT_BUTT: case ID_PCB_HIGHLIGHT_BUTT:
return PCB_ACTIONS::highlightNetCursor.MakeEvent(); return PCB_ACTIONS::highlightNetCursor.MakeEvent();

View File

@ -324,6 +324,7 @@ public:
static TOOL_ACTION zoomTool; static TOOL_ACTION zoomTool;
static TOOL_ACTION pickerTool; static TOOL_ACTION pickerTool;
static TOOL_ACTION resetCoords; static TOOL_ACTION resetCoords;
static TOOL_ACTION measureTool;
static TOOL_ACTION switchCursor; static TOOL_ACTION switchCursor;
static TOOL_ACTION switchUnits; static TOOL_ACTION switchUnits;
static TOOL_ACTION deleteItemCursor; static TOOL_ACTION deleteItemCursor;