29#include <nlohmann/json.hpp>
52constexpr const char* TYPE_NONE =
"none";
53constexpr const char* TYPE_BOOL =
"bool";
54constexpr const char* TYPE_INT =
"int";
55constexpr const char* TYPE_INT64 =
"int64";
56constexpr const char* TYPE_DOUBLE =
"double";
57constexpr const char* TYPE_STRING =
"string";
58constexpr const char* TYPE_KIID =
"kiid";
59constexpr const char* TYPE_VECTOR2I =
"vec2";
60constexpr const char* TYPE_BOX2I =
"box2";
61constexpr const char* TYPE_COLOR =
"color";
62constexpr const char* TYPE_LAYER =
"layer";
63constexpr const char* TYPE_ENUM =
"enum";
64constexpr const char* TYPE_POLYSET =
"polyset";
67const char* typeTag( DIFF_VALUE::T aType )
71 case DIFF_VALUE::T::NONE:
return TYPE_NONE;
72 case DIFF_VALUE::T::BOOL:
return TYPE_BOOL;
73 case DIFF_VALUE::T::INT:
return TYPE_INT;
74 case DIFF_VALUE::T::INT64:
return TYPE_INT64;
75 case DIFF_VALUE::T::DOUBLE:
return TYPE_DOUBLE;
76 case DIFF_VALUE::T::STRING:
return TYPE_STRING;
77 case DIFF_VALUE::T::KIID:
return TYPE_KIID;
78 case DIFF_VALUE::T::VECTOR2I:
return TYPE_VECTOR2I;
79 case DIFF_VALUE::T::BOX2I:
return TYPE_BOX2I;
80 case DIFF_VALUE::T::COLOR:
return TYPE_COLOR;
81 case DIFF_VALUE::T::LAYER:
return TYPE_LAYER;
82 case DIFF_VALUE::T::ENUM:
return TYPE_ENUM;
83 case DIFF_VALUE::T::POLYGON_SET:
return TYPE_POLYSET;
90DIFF_VALUE::T typeFromTag(
const std::string& aTag )
92 if( aTag == TYPE_BOOL )
return DIFF_VALUE::T::BOOL;
93 if( aTag == TYPE_INT )
return DIFF_VALUE::T::INT;
94 if( aTag == TYPE_INT64 )
return DIFF_VALUE::T::INT64;
95 if( aTag == TYPE_DOUBLE )
return DIFF_VALUE::T::DOUBLE;
96 if( aTag == TYPE_STRING )
return DIFF_VALUE::T::STRING;
97 if( aTag == TYPE_KIID )
return DIFF_VALUE::T::KIID;
98 if( aTag == TYPE_VECTOR2I )
return DIFF_VALUE::T::VECTOR2I;
99 if( aTag == TYPE_BOX2I )
return DIFF_VALUE::T::BOX2I;
100 if( aTag == TYPE_COLOR )
return DIFF_VALUE::T::COLOR;
101 if( aTag == TYPE_LAYER )
return DIFF_VALUE::T::LAYER;
102 if( aTag == TYPE_ENUM )
return DIFF_VALUE::T::ENUM;
103 if( aTag == TYPE_POLYSET )
104 return DIFF_VALUE::T::POLYGON_SET;
106 return DIFF_VALUE::T::NONE;
150 return FromString( std::string( aValue.ToUTF8() ) );
221 v.
m_value = std::move( aValue );
228 return std::get<bool>(
m_value );
234 return std::get<int>(
m_value );
240 return std::get<int64_t>(
m_value );
246 return std::get<double>(
m_value );
252 return wxString::FromUTF8( std::get<std::string>(
m_value ) );
258 return std::get<KIID>(
m_value );
264 return std::get<VECTOR2I>(
m_value );
270 return std::get<BOX2I>(
m_value );
276 return std::get<KIGFX::COLOR4D>(
m_value );
282 return std::get<PCB_LAYER_ID>(
m_value );
288 return std::get<EnumValue>(
m_value );
294 return std::get<PolygonSet>(
m_value );
303 return wxS(
"<none>" );
306 return AsBool() ? wxS(
"true" ) : wxS(
"false" );
309 return wxString::Format( wxS(
"%d" ),
AsInt() );
312 return wxString::Format( wxS(
"%lld" ),
static_cast<long long>(
AsInt64() ) );
315 return wxString::Format( wxS(
"%g" ),
AsDouble() );
321 return AsKiid().AsString();
326 return wxString::Format( wxS(
"(%d, %d)" ), p.
x, p.
y );
332 return wxString::Format( wxS(
"[(%d, %d) %dx%d]" ),
337 return AsColor().ToCSSString();
346 if( !ev.second.empty() )
347 return wxString::FromUTF8( ev.second );
349 return wxString::Format( wxS(
"%d" ), ev.first );
355 int outlineCount = 0;
359 for(
const auto& poly : ps )
364 if( poly.size() > 1 )
365 holeCount +=
static_cast<int>( poly.size() ) - 1;
367 for(
const auto& contour : poly )
368 vertexCount +=
static_cast<int>( contour.size() );
371 return wxString::Format( wxS(
"%d outline(s), %d hole(s), %d vertex(es)" ), outlineCount, holeCount,
376 return wxEmptyString;
391 :
static_cast<double>(
AsInt64() );
415 return wxString::Format( wxS(
"(%s, %s)" ), x, y );
433 j[
"type"] = typeTag(
m_type );
457 j[
"v"] = std::get<std::string>(
m_value );
461 j[
"v"] =
AsKiid().AsStdString();
467 j[
"v"] = { p.
x, p.
y };
481 j[
"v"] = { c.
r, c.
g, c.
b, c.
a };
486 j[
"v"] =
static_cast<int>(
AsLayer() );
493 j[
"label"] = ev.second;
500 nlohmann::json polygons = nlohmann::json::array();
502 for(
const auto& poly : ps )
504 nlohmann::json contours = nlohmann::json::array();
506 for(
const auto& contour : poly )
508 nlohmann::json points = nlohmann::json::array();
511 points.push_back( { pt.x, pt.y } );
513 contours.push_back( std::move( points ) );
516 polygons.push_back( std::move( contours ) );
519 j[
"v"] = std::move( polygons );
533 if( !aJson.contains(
"type" ) )
536 T type = typeFromTag( aJson.at(
"type" ).get<std::string>() );
544 return FromBool( aJson.at(
"v" ).get<
bool>() );
547 return FromInt( aJson.at(
"v" ).get<
int>() );
550 return FromInt64( aJson.at(
"v" ).get<int64_t>() );
553 return FromDouble( aJson.at(
"v" ).get<
double>() );
556 return FromString( aJson.at(
"v" ).get<std::string>() );
559 return FromKiid(
KIID( aJson.at(
"v" ).get<std::string>() ) );
563 const auto& arr = aJson.at(
"v" );
569 const auto& arr = aJson.at(
"v" );
570 BOX2I b(
VECTOR2I( arr.at( 0 ).get<
int>(), arr.at( 1 ).get<
int>() ),
571 VECTOR2I( arr.at( 2 ).get<
int>(), arr.at( 3 ).get<
int>() ) );
577 const auto& arr = aJson.at(
"v" );
579 arr.at( 1 ).get<
double>(),
580 arr.at( 2 ).get<
double>(),
581 arr.at( 3 ).get<
double>() ) );
591 if( aJson.contains(
"label" ) )
592 label = aJson.at(
"label" ).get<std::string>();
594 return FromEnum( aJson.at(
"v" ).get<
int>(), label );
600 const auto& polygons = aJson.at(
"v" );
602 for(
const auto& poly : polygons )
604 std::vector<std::vector<VECTOR2I>> contours;
606 for(
const auto& contour : poly )
608 std::vector<VECTOR2I> points;
610 for(
const auto& pt : contour )
611 points.emplace_back( pt.at( 0 ).get<
int>(), pt.at( 1 ).get<
int>() );
613 contours.push_back( std::move( points ) );
616 ps.push_back( std::move( contours ) );
635 return { {
"name",
name },
636 {
"before",
before.ToJson() },
637 {
"after",
after.ToJson() } };
644 d.
name = aJson.at(
"name" ).get<wxString>();
653 return id == aOther.
id
666 j[
"id"] =
id.AsString();
670 nlohmann::json props = nlohmann::json::array();
673 props.push_back( p.ToJson() );
675 j[
"properties"] = std::move( props );
677 j[
"bbox"] = {
bbox.GetX(),
bbox.GetY(),
bbox.GetWidth(),
bbox.GetHeight() };
682 nlohmann::json kids = nlohmann::json::array();
685 kids.push_back( c.ToJson() );
687 j[
"children"] = std::move( kids );
696 c.
id =
KIID_PATH( aJson.at(
"id" ).get<wxString>() );
697 c.
typeName = aJson.at(
"typeName" ).get<wxString>();
700 for(
const auto& p : aJson.at(
"properties" ) )
703 const auto& b = aJson.at(
"bbox" );
705 VECTOR2I( b.at( 2 ).get<
int>(), b.at( 3 ).get<
int>() ) );
707 if( aJson.contains(
"refdes" ) )
708 c.
refdes = aJson.at(
"refdes" ).get<wxString>();
710 for(
const auto& kid : aJson.at(
"children" ) )
723 nlohmann::json arr = nlohmann::json::array();
726 arr.push_back( c.ToJson() );
728 j[
"changes"] = std::move( arr );
736 d.
path = aJson.at(
"path" ).get<wxString>();
737 d.
docType = aJson.at(
"docType" ).get<wxString>();
739 for(
const auto& c : aJson.at(
"changes" ) )
760 nlohmann::json arr = nlohmann::json::array();
763 arr.push_back( d.ToJson() );
765 return { {
"documents", std::move( arr ) } };
773 for(
const auto& d : aJson.at(
"documents" ) )
803 throw std::invalid_argument(
"Unknown CHANGE_KIND tag: " + aKind );
808 const wxString& aLabelB,
EDA_UNITS aUnits,
811 std::ostringstream ss;
812 ss <<
"diff " << aLabelA.ToStdString() <<
" " << aLabelB.ToStdString() <<
"\n";
813 ss << aDiff.
changes.size() <<
" change(s)\n";
815 std::function<void(
const ITEM_CHANGE&,
int )> writeChange;
817 writeChange = [&](
const ITEM_CHANGE& aChange,
int aDepth )
819 std::string indent(
static_cast<std::size_t
>( 2 + aDepth * 2 ),
' ' );
822 <<
" " << aChange.
id.
AsString().ToStdString();
824 if( aChange.
refdes.has_value() )
825 ss <<
" [" << aChange.
refdes->ToStdString() <<
"]";
829 std::string propIndent(
static_cast<std::size_t
>( 4 + aDepth * 2 ),
' ' );
833 ss << propIndent << p.
name.ToStdString() <<
": "
839 writeChange( child, aDepth + 1 );
851 if( aOutputPath.IsEmpty() )
853 return fwrite( aContent.data(), 1, aContent.size(), stdout ) == aContent.size()
854 && ferror( stdout ) == 0;
857 wxFile out( aOutputPath, wxFile::write );
859 if( !out.IsOpened() )
862 out.Write( aContent.data(), aContent.size() );
constexpr coord_type GetY() const
constexpr size_type GetWidth() const
constexpr coord_type GetX() const
constexpr size_type GetHeight() const
static DIFF_VALUE FromLayer(PCB_LAYER_ID aLayer)
static DIFF_VALUE FromDouble(double aValue)
static DIFF_VALUE FromEnum(int aValue, const std::string &aLabel)
KIGFX::COLOR4D AsColor() const
static DIFF_VALUE FromInt64(int64_t aValue)
static DIFF_VALUE FromInt(int aValue)
static DIFF_VALUE FromBox2I(const BOX2I &aValue)
const PolygonSet & AsPolygonSet() const
VECTOR2I AsVector2I() const
bool operator==(const DIFF_VALUE &aOther) const
std::pair< int, std::string > EnumValue
Enum payload: (numeric_value, label) so JSON output is human-readable while the integer is the canoni...
wxString AsString() const
static DIFF_VALUE FromKiid(const KIID &aValue)
static DIFF_VALUE FromColor(const KIGFX::COLOR4D &aValue)
wxString ToDisplayString() const
Human-readable representation with no unit context.
nlohmann::json ToJson() const
static DIFF_VALUE FromBool(bool aValue)
static DIFF_VALUE FromString(const wxString &aValue)
std::vector< std::vector< std::vector< VECTOR2I > > > PolygonSet
static DIFF_VALUE FromJson(const nlohmann::json &aJson)
DISPLAY_HINT m_displayHint
Presentation aid only; excluded from operator== and JSON (see DISPLAY_HINT).
static DIFF_VALUE FromPolygonSet(PolygonSet aValue)
PCB_LAYER_ID AsLayer() const
static DIFF_VALUE FromVector2I(const VECTOR2I &aValue)
A color representation with 4 components: red, green, blue, alpha.
wxString AsString() const
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
PCB_LAYER_ID
A quick note on layer IDs:
KICOMMON_API wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
KICOMMON_API wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Return the string from aValue according to aUnits (inch, mm ...) for display.
CHANGE_KIND
Coarse classification of a single item-level change between two documents.
CHANGE_KIND ChangeKindFromString(const std::string &aKind)
bool WriteDiffOutput(const std::string &aContent, const wxString &aOutputPath)
Write diff/merge text output to aOutputPath, or to stdout when the path is empty.
@ ANGLE
Angle in degrees (PT_DEGREE)
const char * ChangeKindToString(CHANGE_KIND aKind)
std::string FormatDiffAsText(const DOCUMENT_DIFF &aDiff, const wxString &aLabelA, const wxString &aLabelB, EDA_UNITS aUnits, const EDA_IU_SCALE &aScale)
Render a DOCUMENT_DIFF as the human-readable text report shared by the diff jobs and CLI: a diff <lab...
The full set of changes between two parsed documents of one type.
nlohmann::json ToJson() const
static DOCUMENT_DIFF FromJson(const nlohmann::json &aJson)
std::vector< ITEM_CHANGE > changes
One change record on a single item.
static ITEM_CHANGE FromJson(const nlohmann::json &aJson)
bool operator==(const ITEM_CHANGE &aOther) const
std::vector< PROPERTY_DELTA > properties
std::optional< wxString > refdes
std::vector< ITEM_CHANGE > children
nlohmann::json ToJson() const
Aggregated project-level diff covering many documents.
nlohmann::json ToJson() const
std::vector< DOCUMENT_DIFF > documents
static PROJECT_DIFF FromJson(const nlohmann::json &aJson)
Single (name, before, after) triple for one mutated property on an item.
static PROPERTY_DELTA FromJson(const nlohmann::json &aJson)
nlohmann::json ToJson() const
bool operator==(const PROPERTY_DELTA &aOther) const
VECTOR2< int32_t > VECTOR2I