Skip to content

Commit

Permalink
Version 0.2 - XLSX export now generates fully playable puzzles.
Browse files Browse the repository at this point in the history
#fix #3 Included option to use experimental (yet to be committed) fork of libxlsxwriter with conditional formatting support (at least enough for the use case here).

Add version number in title.
  • Loading branch information
TricksterGuy committed Jul 3, 2020
1 parent 55c9913 commit 0da544c
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 14 deletions.
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ project (ppe)
include(FindPkgConfig)

option(ENABLE_XLSX "Enable xlsx export option. (Requires libxlsxwriter)" OFF)
option(ENABLE_EXPERIMENTAL_LIBXLSXWRITER "Enable experimentatal libxlsxwriter functions (conditional formatting)" OFF)

# Require C++17 build
if(NOT CMAKE_CXX_FLAGS MATCHES "-std=(c|gnu)\\+\\+17")
Expand Down Expand Up @@ -55,11 +56,14 @@ set(SRC_ppe

set(wxWidgets_USE_LIBS core base)
find_package(wxWidgets REQUIRED)

# Use protobuf-lite because its smaller and we don't require the whole library.
pkg_search_module(PROTOBUF REQUIRED protobuf-lite)

if(ENABLE_XLSX)
# libxlsxwriter doesn't have a pkg-config definition or FindCMake module.
set(XLSX_INCLUDEDIR "")
set(XLSX_LIBRARIES "xlsxwriter")
set(XLSX_LIBRARIES "xlsxwriter" "z")

set(SRC_ppe
${SRC_ppe}
Expand All @@ -68,6 +72,10 @@ if(ENABLE_XLSX)
add_definitions(-DENABLE_XLSX=1)
endif(ENABLE_XLSX)

if(ENABLE_EXPERIMENTAL_LIBXLSXWRITER)
add_definitions(-DENABLE_EXPERIMENTAL_LIBXLSXWRITER=1)
endif(ENABLE_EXPERIMENTAL_LIBXLSXWRITER)

add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/gen/Picross.pb.cc ${CMAKE_CURRENT_SOURCE_DIR}/src/gen/Picross.pb.h
COMMAND protoc --cpp_out=../src/gen Picross.proto
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/gen/Picross.proto
Expand Down
286 changes: 275 additions & 11 deletions src/exporters/XlsxExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,75 @@ void ExportXlsxLight(lxw_workbook* workbook, const Picross& picross);
void WriteType(lxw_workbook* workbook, lxw_worksheet* worksheet, const std::string& type, int r, int c, int size);
void WriteHints(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int layer, int r, int c, int size);
void WriteBoard(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c);
void WriteSolutionBoard(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c, std::vector<std::pair<int, int>> boards);
void WriteExtraHints(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int layer, int r, int c, int size);

void WriteClassicFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c);
void WriteGrayscaleFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c);
void WriteLightFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c);
void WritePaintingFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c);
int GetMaxHintsSize(const Picross& picross);

std::string GetColumnName(int column)
{
std::stringstream ss;
column += 1;
while (column > 0)
{
column--;
ss << static_cast<char>('A' + column % 26);
column /= 26;
}
std::string s = ss.str();

std::reverse(s.begin(), s.end());
return s;
}

std::string GetCell(int column, int row)
{
std::stringstream ss;
ss << GetColumnName(column);
ss << (row+1);
return ss.str();
}

std::string GetRange(int column, int row, int width, int height)
{
std::stringstream ss;
if (column >= 26)
ss << static_cast<char>(column / 26 - 1 + 'A');
ss << static_cast<char>(column % 26 + 'A');
ss << (row + 1) << ":";
int end_column = column + width;
if (end_column >= 26)
ss << static_cast<char>(end_column / 26 - 1 + 'A');
ss << static_cast<char>(end_column % 26 + 'A');
ss << (row + 1 + height);
ss << GetCell(column, row) << ":" << GetCell(column + width, row + height);
return ss.str();
}

std::string GetLightFormula(const std::vector<std::pair<int, int>>& boards, int r, int c, int bpc)
{
std::stringstream ss;
ss << "=";
for (unsigned int i = 0; i < boards.size(); i++)
{
auto [br, bc] = boards[i];
ss << GetCell(bc + c, br + r);
if (i != 0)
ss << " * " << (1 << (bpc * i));
if (i != boards.size() - 1)
ss << " + ";
}
return ss.str();
}

std::string GetPaintingFormula(const std::vector<std::pair<int, int>>& boards, int r, int c, int bpc)
{
std::stringstream ss;
ss << "=";
for (unsigned int i = 0; i < boards.size(); i++)
{
auto [br, bc] = boards[i];
ss << GetCell(bc + c, br + r);
if (i != 0)
ss << " * " << (1 << i);
if (i != boards.size() - 1)
ss << " + ";
}
return ss.str();
}

Expand Down Expand Up @@ -63,6 +117,7 @@ void ExportXlsxClassic(lxw_workbook* workbook, const Picross& picross)
int offset = GetMaxHintsSize(picross);

WriteHints(workbook, worksheet, picross, 0, 0, 0, offset);
WriteClassicFormatting(workbook, worksheet, picross, offset, offset);
WriteBoard(workbook, worksheet, picross, offset, offset);

worksheet_set_column(worksheet, 0, offset + picross.GetWidth(), 2, nullptr);
Expand All @@ -76,6 +131,7 @@ void ExportXlsxGrayscale(lxw_workbook* workbook, const Picross& picross)

WriteHints(workbook, worksheet, picross, 0, 0, 0, offset);
WriteBoard(workbook, worksheet, picross, offset, offset);
WriteGrayscaleFormatting(workbook, worksheet, picross, offset, offset);
if (picross.GetBpc() > 1)
WriteExtraHints(workbook, worksheet, picross, 0, offset, offset, offset);

Expand Down Expand Up @@ -111,7 +167,13 @@ void ExportXlsxLight(lxw_workbook* workbook, const Picross& picross)
WriteExtraHints(workbook, worksheet, picross, 2, 2 * offset + h + extra + 1, offset, offset);
}

WriteBoard(workbook, worksheet, picross , 2 * offset + h + extra + 1, 2 * offset + w + extra + 1);
std::vector<std::pair<int, int>> boards = {
{offset, offset},
{offset, 2 * offset + w + extra + 1},
{2 * offset + h + extra + 1, offset}
};
WriteSolutionBoard( workbook, worksheet, picross , 2 * offset + h + extra + 1, 2 * offset + w + extra + 1, boards);
WriteLightFormatting(workbook, worksheet, picross , 2 * offset + h + extra + 1, 2 * offset + w + extra + 1);

worksheet_set_column(worksheet, 0, 2 * offset + 2 * (w + extra) + 1, 2, nullptr);
}
Expand Down Expand Up @@ -144,7 +206,16 @@ void ExportXlsxPainting(lxw_workbook* workbook, const Picross& picross)
WriteHints(workbook, worksheet, picross, 4, 2 * offset + 2 * (h + 1), 0, offset);
WriteBoard(workbook, worksheet, picross , 3 * offset + 2 * (h + 1), offset);

WriteBoard(workbook, worksheet, picross , 3 * offset + 2 * (h + 1), 2 * offset + w + 1);

std::vector<std::pair<int, int>> boards = {
{offset, offset},
{offset, 2 * offset + w + 1},
{2 * offset + h + 1, offset},
{2 * offset + h + 1, 2 * offset + w + 1},
{3 * offset + 2 * (h + 1), offset}
};
WriteSolutionBoard(workbook, worksheet, picross , 3 * offset + 2 * (h + 1), 2 * offset + w + 1, boards);
WritePaintingFormatting(workbook, worksheet, picross , 3 * offset + 2 * (h + 1), 2 * offset + w + 1);

worksheet_set_column(worksheet, 0, 2 * offset + 2 * w + 2, 2, nullptr);
}
Expand Down Expand Up @@ -266,6 +337,78 @@ void WriteBoard(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross&
}
}

void WriteSolutionBoard(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c, std::vector<std::pair<int, int>> boards)
{
int bpc = picross.GetBpc();
std::function<std::string(const std::vector<std::pair<int, int>>& boards, int r, int c, int bpc)> formula;
if (picross.GetType() == PicrossPuzzle::TYPE_RGB)
formula = GetLightFormula;
else if (picross.GetType() == PicrossPuzzle::TYPE_RBY)
formula = GetPaintingFormula;

for (int i = 0; i < picross.GetWidth(); i++)
{
auto* top_border = workbook_add_format(workbook);
auto* bottom_border = workbook_add_format(workbook);

format_set_top(top_border, LXW_BORDER_THIN);
format_set_align(top_border, LXW_ALIGN_CENTER);
format_set_bottom(bottom_border, LXW_BORDER_THIN);
format_set_align(bottom_border, LXW_ALIGN_CENTER);

worksheet_write_formula_num(worksheet, r, c + i, formula(boards, 0, i, bpc).c_str(), top_border, 0);
worksheet_write_formula_num(worksheet, r + picross.GetHeight() - 1, c + i, formula(boards, picross.GetHeight() - 1, i, bpc).c_str(), bottom_border, 0);
}
for (int i = 0; i < picross.GetHeight(); i++)
{
auto* left_border = workbook_add_format(workbook);
auto* right_border = workbook_add_format(workbook);

format_set_left(left_border, LXW_BORDER_THIN);
format_set_align(left_border, LXW_ALIGN_CENTER);
format_set_right(right_border, LXW_BORDER_THIN);
format_set_align(right_border, LXW_ALIGN_CENTER);

worksheet_write_formula_num(worksheet, r + i, c, formula(boards, i, 0, bpc).c_str(), left_border, 0);
worksheet_write_formula_num(worksheet, r + i, c + picross.GetWidth() - 1, formula(boards, i, picross.GetWidth() - 1, bpc).c_str(), right_border, 0);
}

auto* lt_border = workbook_add_format(workbook);
auto* rt_border = workbook_add_format(workbook);
auto* lb_border = workbook_add_format(workbook);
auto* rb_border = workbook_add_format(workbook);

format_set_left(lt_border, LXW_BORDER_THIN);
format_set_top(lt_border, LXW_BORDER_THIN);
format_set_align(lt_border, LXW_ALIGN_CENTER);

format_set_left(lb_border, LXW_BORDER_THIN);
format_set_bottom(lb_border, LXW_BORDER_THIN);
format_set_align(lb_border, LXW_ALIGN_CENTER);

format_set_right(rt_border, LXW_BORDER_THIN);
format_set_top(rt_border, LXW_BORDER_THIN);
format_set_align(rt_border, LXW_ALIGN_CENTER);

format_set_right(rb_border, LXW_BORDER_THIN);
format_set_bottom(rb_border, LXW_BORDER_THIN);
format_set_align(rb_border, LXW_ALIGN_CENTER);

worksheet_write_formula_num(worksheet, r, c, formula(boards, 0, 0, bpc).c_str(), lt_border, 0);
worksheet_write_formula_num(worksheet, r + picross.GetHeight() - 1, c, formula(boards, picross.GetHeight() - 1, 0, bpc).c_str(), lb_border, 0);
worksheet_write_formula_num(worksheet, r, c + picross.GetWidth() - 1, formula(boards, 0, picross.GetWidth() - 1, bpc).c_str(), rt_border, 0);
worksheet_write_formula_num(worksheet, r + picross.GetHeight() - 1, c + picross.GetWidth() - 1, formula(boards, picross.GetHeight() - 1, picross.GetWidth() - 1, bpc).c_str(), rb_border, 0);


auto* center = workbook_add_format(workbook);
format_set_align(center, LXW_ALIGN_CENTER);
for (int i = 1; i < picross.GetHeight() - 1; i++)
{
for (int j = 1; j < picross.GetWidth() - 1; j++)
worksheet_write_formula_num(worksheet, r + i, c + j, formula(boards, i, j, bpc).c_str(), center, 0);
}
}

void WriteExtraHints(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int layer, int r, int c, int size)
{
const auto& rows = picross.GetRowExtraHints().at(layer);
Expand Down Expand Up @@ -301,3 +444,124 @@ void WriteExtraHints(lxw_workbook* workbook, lxw_worksheet* worksheet, const Pic
worksheet_write_number(worksheet, r + i, c + picross.GetWidth() + shadings, shading_rows[i], center);
}
}

void WriteClassicFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c)
{
#ifdef ENABLE_EXPERIMENTAL_LIBXLSXWRITER
auto* black = workbook_add_format(workbook);
format_set_pattern(black, LXW_PATTERN_SOLID);
format_set_bg_color(black, LXW_COLOR_BLACK);
format_set_fg_color(black, LXW_COLOR_BLACK);
auto* cf = lxw_conditional_format_new();
lxw_conditional_format_cell(cf, LXW_CRITERIA_EQUAL, 1, black);
worksheet_conditional_format(worksheet, r, c, r + picross.GetHeight() - 1, c + picross.GetWidth() - 1, cf);
#endif
}

void WriteGrayscaleFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c)
{
#ifdef ENABLE_EXPERIMENTAL_LIBXLSXWRITER
int shadings = 1 << picross.GetBpc();
for (int i = 1; i < shadings; i++)
{
unsigned int cval = 255 * (shadings - i) / shadings;
unsigned int color = cval << 16 | cval << 8 | cval;

auto* format = workbook_add_format(workbook);
format_set_pattern(format, LXW_PATTERN_SOLID);
format_set_bg_color(format, color);
format_set_fg_color(format, color);
auto* cf = lxw_conditional_format_new();
lxw_conditional_format_cell(cf, LXW_CRITERIA_EQUAL, i, format);
worksheet_conditional_format(worksheet, r, c, r + picross.GetHeight() - 1, c + picross.GetWidth() - 1, cf);
}
#endif
}

void WriteLightFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int row, int column)
{
#ifdef ENABLE_EXPERIMENTAL_LIBXLSXWRITER
int bpc = picross.GetBpc();
int shadings = 1 << picross.GetBpc();
int colorbits = 256 / (1 << bpc);

for (int r = 0; r < shadings; r++)
{
for (int b = 0; b < shadings; b++)
{
for (int g = 0; g < shadings; g++)
{
unsigned int rval = r * colorbits;
unsigned int gval = g * colorbits;
unsigned int bval = b * colorbits;
unsigned int color = rval << 16 | gval << 8 | bval;

auto* format = workbook_add_format(workbook);
format_set_pattern(format, LXW_PATTERN_SOLID);
format_set_bg_color(format, color);
format_set_fg_color(format, color);
auto* cf = lxw_conditional_format_new();
lxw_conditional_format_cell(cf, LXW_CRITERIA_EQUAL, r | g << bpc | b << (bpc*2), format);
worksheet_conditional_format(worksheet, row, column, row + picross.GetHeight() - 1, column + picross.GetWidth() - 1, cf);
}
}
}
#endif
}

void WritePaintingFormatting(lxw_workbook* workbook, lxw_worksheet* worksheet, const Picross& picross, int r, int c)
{
#ifdef ENABLE_EXPERIMENTAL_LIBXLSXWRITER
constexpr uint8_t BLACK = 1;
constexpr uint8_t WHITE = 2;
constexpr uint8_t RED = 4;
constexpr uint8_t BLUE = 8;
constexpr uint8_t YELLOW = 16;
constexpr uint8_t PURPLE = 12;
constexpr uint8_t ORANGE = 20;
constexpr uint8_t GREEN = 24;
constexpr uint8_t GRAY = 28;

struct {
uint32_t r;
uint32_t g;
uint32_t b;
} color = {0, 0, 0};

for (int i = 1; i < 32; i++)
{
if ((i & GRAY) == GRAY)
color = {128, 128, 128};
else if ((i & PURPLE) == PURPLE)
color = {128, 0, 128};
else if ((i & ORANGE) == ORANGE)
color = {255, 128, 0};
else if ((i & GREEN) == GREEN)
color = {0, 128, 0};
else if ((i & RED) == RED)
color = {255, 0, 0};
else if ((i & BLUE) == BLUE)
color = {0, 0, 255};
else if ((i & YELLOW) == YELLOW)
color = {255, 255, 0};
else if (i == WHITE)
color = {255, 255, 255};
else if (i == BLACK)
color = {0, 0, 0};

if ((i & WHITE) == WHITE)
color = {color.r / 2 + 128, color.g / 2 + 128, color.b / 2 + 128};
if ((i & BLACK) == BLACK)
color = {color.r / 2, color.g / 2, color.b / 2};

uint32_t cval = color.r << 16 | color.g << 8 | color.b;
auto* format = workbook_add_format(workbook);
format_set_pattern(format, LXW_PATTERN_SOLID);
format_set_bg_color(format, cval);
format_set_fg_color(format, cval);
auto* cf = lxw_conditional_format_new();
lxw_conditional_format_cell(cf, LXW_CRITERIA_EQUAL, i, format);
worksheet_conditional_format(worksheet, r, c, r + picross.GetHeight() - 1, c + picross.GetWidth() - 1, cf);
}
#endif
}
7 changes: 5 additions & 2 deletions src/gui/PicrossApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
#include <wx/config.h>
#include <wx/dialog.h>

#include "cpercep.hpp"
#include "cpercep.hpp"

constexpr const char* FULL_VERSION_STRING = "0.2";

void init_palette();

Expand All @@ -37,7 +39,8 @@ bool PicrossApp::OnInit()

wxInitAllImageHandlers();

frame = new PicrossFrame(NULL);
frame = new PicrossFrame(NULL);
frame->SetTitle(wxString::Format("Picross Puzzle Exporter v%s", FULL_VERSION_STRING));
SetTopWindow(frame);
frame->Centre();
frame->Show();
Expand Down

0 comments on commit 0da544c

Please sign in to comment.