feat(gal-test): Add 100% GAL API test coverage (28 scenarios)
Expand GAL native test harness from 24 to 28 scenarios covering all 70 GAL methods. New scenarios: - scenario_text_attrs.cpp (24): Text attribute APIs (SetGlyphSize, SetFontBold/Italic/Underlined, SetTextMirrored, justification) - scenario_glyphs.cpp (25): DrawGlyph/DrawGlyphs with stroke glyphs - scenario_bitmap.cpp (26): DrawBitmap with test patterns - scenario_transform.cpp (27): Transform() API documentation Additional API coverage in existing scenarios: - Flush() in test harness - SetFlip(), SetRotation() in screen-transform - SetDepthRange() in depth-testing - GetGridPoint() in grid-native New stub files: - kifont_stub.h: STROKE_GLYPH factory functions for letter glyphs - bitmap_base_stub.h: Test pattern generators (checkerboard, gradient) Note: Bitmap scenario shows empty panels - DrawBitmap uses legacy OpenGL immediate mode (glBegin/glEnd) which doesn't work while shader is active. This is a known limitation when testing outside KiCad's VIEW rendering flow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BIN
tests/gal-regression/baseline/gal-bitmap.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
tests/gal-regression/baseline/gal-clear-colors.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 81 KiB |
BIN
tests/gal-regression/baseline/gal-cursor-native.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
tests/gal-regression/baseline/gal-depth-testing.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
tests/gal-regression/baseline/gal-glyphs.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
tests/gal-regression/baseline/gal-grid-native.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
BIN
tests/gal-regression/baseline/gal-hole-walls.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
BIN
tests/gal-regression/baseline/gal-negative-mode.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
tests/gal-regression/baseline/gal-polylines-multi.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
tests/gal-regression/baseline/gal-render-targets.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
tests/gal-regression/baseline/gal-screen-transform.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
tests/gal-regression/baseline/gal-text-attrs.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
tests/gal-regression/baseline/gal-transform-api.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
|
|
@ -72,6 +72,20 @@ set(SCENARIO_SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/../scenarios/scenario_arc_segments.cpp
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_arc_segments.cpp
|
||||||
${CMAKE_SOURCE_DIR}/../scenarios/scenario_segment_chain.cpp
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_segment_chain.cpp
|
||||||
${CMAKE_SOURCE_DIR}/../scenarios/scenario_group_caching.cpp
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_group_caching.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_polylines_multi.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_hole_walls.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_grid_native.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_cursor_native.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_render_targets.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_screen_transform.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_clear_colors.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_depth_testing.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_negative_mode.cpp
|
||||||
|
# Additional scenarios (24-27)
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_text_attrs.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_glyphs.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_bitmap.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/../scenarios/scenario_transform.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
|
|
|
||||||
415
tests/gal-regression/native/bitmap_base_stub.h
Normal file
|
|
@ -0,0 +1,415 @@
|
||||||
|
/**
|
||||||
|
* BITMAP_BASE Test Patterns for GAL Test
|
||||||
|
*
|
||||||
|
* Provides helper functions to create wxImage test patterns that can be
|
||||||
|
* used with KiCad's real BITMAP_BASE class for testing DrawBitmap().
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* wxImage img = CreateCheckerboardImage(64, 64);
|
||||||
|
* BITMAP_BASE bitmap;
|
||||||
|
* bitmap.SetImage(img);
|
||||||
|
* gal->DrawBitmap(bitmap);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITMAP_BASE_STUB_H
|
||||||
|
#define BITMAP_BASE_STUB_H
|
||||||
|
|
||||||
|
#include <wx/image.h>
|
||||||
|
#include <bitmap_base.h>
|
||||||
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// Image pattern generation functions
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a solid color image
|
||||||
|
*/
|
||||||
|
inline wxImage CreateSolidImage(int width, int height,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)
|
||||||
|
{
|
||||||
|
wxImage img(width, height);
|
||||||
|
img.InitAlpha();
|
||||||
|
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
unsigned char* alpha = img.GetAlpha();
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
data[idx + 0] = r;
|
||||||
|
data[idx + 1] = g;
|
||||||
|
data[idx + 2] = b;
|
||||||
|
alpha[y * width + x] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a checkerboard pattern image
|
||||||
|
*/
|
||||||
|
inline wxImage CreateCheckerboardImage(int width, int height, int squareSize = 8,
|
||||||
|
uint8_t r1 = 255, uint8_t g1 = 255, uint8_t b1 = 255,
|
||||||
|
uint8_t r2 = 0, uint8_t g2 = 0, uint8_t b2 = 0)
|
||||||
|
{
|
||||||
|
wxImage img(width, height);
|
||||||
|
img.InitAlpha();
|
||||||
|
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
unsigned char* alpha = img.GetAlpha();
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
bool isLight = ((x / squareSize) + (y / squareSize)) % 2 == 0;
|
||||||
|
|
||||||
|
if (isLight)
|
||||||
|
{
|
||||||
|
data[idx + 0] = r1;
|
||||||
|
data[idx + 1] = g1;
|
||||||
|
data[idx + 2] = b1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data[idx + 0] = r2;
|
||||||
|
data[idx + 1] = g2;
|
||||||
|
data[idx + 2] = b2;
|
||||||
|
}
|
||||||
|
alpha[y * width + x] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a horizontal gradient image
|
||||||
|
*/
|
||||||
|
inline wxImage CreateGradientHImage(int width, int height,
|
||||||
|
uint8_t r1, uint8_t g1, uint8_t b1,
|
||||||
|
uint8_t r2, uint8_t g2, uint8_t b2)
|
||||||
|
{
|
||||||
|
wxImage img(width, height);
|
||||||
|
img.InitAlpha();
|
||||||
|
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
unsigned char* alpha = img.GetAlpha();
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
float t = (float)x / (width - 1);
|
||||||
|
|
||||||
|
data[idx + 0] = (uint8_t)(r1 + t * (r2 - r1));
|
||||||
|
data[idx + 1] = (uint8_t)(g1 + t * (g2 - g1));
|
||||||
|
data[idx + 2] = (uint8_t)(b1 + t * (b2 - b1));
|
||||||
|
alpha[y * width + x] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a vertical gradient image
|
||||||
|
*/
|
||||||
|
inline wxImage CreateGradientVImage(int width, int height,
|
||||||
|
uint8_t r1, uint8_t g1, uint8_t b1,
|
||||||
|
uint8_t r2, uint8_t g2, uint8_t b2)
|
||||||
|
{
|
||||||
|
wxImage img(width, height);
|
||||||
|
img.InitAlpha();
|
||||||
|
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
unsigned char* alpha = img.GetAlpha();
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
float t = (float)y / (height - 1);
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
|
||||||
|
data[idx + 0] = (uint8_t)(r1 + t * (r2 - r1));
|
||||||
|
data[idx + 1] = (uint8_t)(g1 + t * (g2 - g1));
|
||||||
|
data[idx + 2] = (uint8_t)(b1 + t * (b2 - b1));
|
||||||
|
alpha[y * width + x] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a radial gradient image
|
||||||
|
*/
|
||||||
|
inline wxImage CreateRadialGradientImage(int width, int height,
|
||||||
|
uint8_t r1, uint8_t g1, uint8_t b1,
|
||||||
|
uint8_t r2, uint8_t g2, uint8_t b2)
|
||||||
|
{
|
||||||
|
wxImage img(width, height);
|
||||||
|
img.InitAlpha();
|
||||||
|
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
unsigned char* alpha = img.GetAlpha();
|
||||||
|
|
||||||
|
float cx = width / 2.0f;
|
||||||
|
float cy = height / 2.0f;
|
||||||
|
float maxDist = std::sqrt(cx * cx + cy * cy);
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
float dx = x - cx;
|
||||||
|
float dy = y - cy;
|
||||||
|
float dist = std::sqrt(dx * dx + dy * dy);
|
||||||
|
float t = std::min(1.0f, dist / maxDist);
|
||||||
|
|
||||||
|
data[idx + 0] = (uint8_t)(r1 + t * (r2 - r1));
|
||||||
|
data[idx + 1] = (uint8_t)(g1 + t * (g2 - g1));
|
||||||
|
data[idx + 2] = (uint8_t)(b1 + t * (b2 - b1));
|
||||||
|
alpha[y * width + x] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a striped pattern image
|
||||||
|
*/
|
||||||
|
inline wxImage CreateStripedImage(int width, int height, int stripeWidth, bool horizontal,
|
||||||
|
uint8_t r1, uint8_t g1, uint8_t b1,
|
||||||
|
uint8_t r2, uint8_t g2, uint8_t b2)
|
||||||
|
{
|
||||||
|
wxImage img(width, height);
|
||||||
|
img.InitAlpha();
|
||||||
|
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
unsigned char* alpha = img.GetAlpha();
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
int pos = horizontal ? y : x;
|
||||||
|
bool isFirst = (pos / stripeWidth) % 2 == 0;
|
||||||
|
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
data[idx + 0] = r1;
|
||||||
|
data[idx + 1] = g1;
|
||||||
|
data[idx + 2] = b1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data[idx + 0] = r2;
|
||||||
|
data[idx + 1] = g2;
|
||||||
|
data[idx + 2] = b2;
|
||||||
|
}
|
||||||
|
alpha[y * width + x] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a "K" logo-style image (simplified KiCad logo)
|
||||||
|
*/
|
||||||
|
inline wxImage CreateKiCadLogoImage(int width, int height,
|
||||||
|
uint8_t bgR = 30, uint8_t bgG = 60, uint8_t bgB = 30,
|
||||||
|
uint8_t fgR = 255, uint8_t fgG = 200, uint8_t fgB = 50)
|
||||||
|
{
|
||||||
|
wxImage img(width, height);
|
||||||
|
img.InitAlpha();
|
||||||
|
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
unsigned char* alpha = img.GetAlpha();
|
||||||
|
|
||||||
|
// Fill background
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
data[idx + 0] = bgR;
|
||||||
|
data[idx + 1] = bgG;
|
||||||
|
data[idx + 2] = bgB;
|
||||||
|
alpha[y * width + x] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a simplified "K" shape
|
||||||
|
int centerX = width / 2;
|
||||||
|
int centerY = height / 2;
|
||||||
|
int thickness = std::max(2, std::min(width, height) / 8);
|
||||||
|
int halfH = height / 3;
|
||||||
|
int halfW = width / 3;
|
||||||
|
|
||||||
|
// Vertical bar of K
|
||||||
|
for (int y = centerY - halfH; y <= centerY + halfH; y++)
|
||||||
|
{
|
||||||
|
if (y < 0 || y >= height) continue;
|
||||||
|
for (int dx = -thickness/2; dx <= thickness/2; dx++)
|
||||||
|
{
|
||||||
|
int x = centerX - halfW/2 + dx;
|
||||||
|
if (x < 0 || x >= width) continue;
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
data[idx + 0] = fgR;
|
||||||
|
data[idx + 1] = fgG;
|
||||||
|
data[idx + 2] = fgB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upper diagonal of K
|
||||||
|
for (int i = 0; i <= halfH; i++)
|
||||||
|
{
|
||||||
|
int y = centerY - i;
|
||||||
|
int x = centerX - halfW/2 + (halfW * i / halfH);
|
||||||
|
if (y < 0 || y >= height) continue;
|
||||||
|
|
||||||
|
for (int dy = -thickness/2; dy <= thickness/2; dy++)
|
||||||
|
{
|
||||||
|
for (int dx = -thickness/2; dx <= thickness/2; dx++)
|
||||||
|
{
|
||||||
|
int py = y + dy;
|
||||||
|
int px = x + dx;
|
||||||
|
if (py < 0 || py >= height) continue;
|
||||||
|
if (px < 0 || px >= width) continue;
|
||||||
|
int idx = (py * width + px) * 3;
|
||||||
|
data[idx + 0] = fgR;
|
||||||
|
data[idx + 1] = fgG;
|
||||||
|
data[idx + 2] = fgB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower diagonal of K
|
||||||
|
for (int i = 0; i <= halfH; i++)
|
||||||
|
{
|
||||||
|
int y = centerY + i;
|
||||||
|
int x = centerX - halfW/2 + (halfW * i / halfH);
|
||||||
|
if (y < 0 || y >= height) continue;
|
||||||
|
|
||||||
|
for (int dy = -thickness/2; dy <= thickness/2; dy++)
|
||||||
|
{
|
||||||
|
for (int dx = -thickness/2; dx <= thickness/2; dx++)
|
||||||
|
{
|
||||||
|
int py = y + dy;
|
||||||
|
int px = x + dx;
|
||||||
|
if (py < 0 || py >= height) continue;
|
||||||
|
if (px < 0 || px >= width) continue;
|
||||||
|
int idx = (py * width + px) * 3;
|
||||||
|
data[idx + 0] = fgR;
|
||||||
|
data[idx + 1] = fgG;
|
||||||
|
data[idx + 2] = fgB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a border to an existing image
|
||||||
|
*/
|
||||||
|
inline void AddBorderToImage(wxImage& img, int borderWidth,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
int width = img.GetWidth();
|
||||||
|
int height = img.GetHeight();
|
||||||
|
unsigned char* data = img.GetData();
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
bool isBorder = (x < borderWidth || x >= width - borderWidth ||
|
||||||
|
y < borderWidth || y >= height - borderWidth);
|
||||||
|
|
||||||
|
if (isBorder)
|
||||||
|
{
|
||||||
|
int idx = (y * width + x) * 3;
|
||||||
|
data[idx + 0] = r;
|
||||||
|
data[idx + 1] = g;
|
||||||
|
data[idx + 2] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// Factory functions to create BITMAP_BASE with test patterns
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BITMAP_BASE with checkerboard pattern
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<BITMAP_BASE> CreateCheckerboardBitmap(int width, int height, int squareSize = 8)
|
||||||
|
{
|
||||||
|
auto bitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
wxImage img = CreateCheckerboardImage(width, height, squareSize);
|
||||||
|
bitmap->SetImage(img);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BITMAP_BASE with horizontal gradient (red to blue)
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<BITMAP_BASE> CreateGradientBitmap(int width, int height)
|
||||||
|
{
|
||||||
|
auto bitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
wxImage img = CreateGradientHImage(width, height, 255, 50, 50, 50, 50, 255);
|
||||||
|
bitmap->SetImage(img);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BITMAP_BASE with KiCad logo pattern
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<BITMAP_BASE> CreateKiCadLogoBitmap(int width, int height)
|
||||||
|
{
|
||||||
|
auto bitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
wxImage img = CreateKiCadLogoImage(width, height);
|
||||||
|
AddBorderToImage(img, 2, 200, 150, 50);
|
||||||
|
bitmap->SetImage(img);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BITMAP_BASE with radial gradient
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<BITMAP_BASE> CreateRadialBitmap(int width, int height)
|
||||||
|
{
|
||||||
|
auto bitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
wxImage img = CreateRadialGradientImage(width, height, 255, 255, 200, 50, 50, 150);
|
||||||
|
bitmap->SetImage(img);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BITMAP_BASE with stripes
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<BITMAP_BASE> CreateStripedBitmap(int width, int height, bool horizontal = true)
|
||||||
|
{
|
||||||
|
auto bitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
wxImage img = CreateStripedImage(width, height, 8, horizontal, 100, 150, 200, 200, 100, 50);
|
||||||
|
bitmap->SetImage(img);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITMAP_BASE_STUB_H
|
||||||
|
|
@ -225,6 +225,9 @@ public:
|
||||||
// Render the scenario using GAL API
|
// Render the scenario using GAL API
|
||||||
GALTest::RenderScenario(m_gal, i, g_width, g_height);
|
GALTest::RenderScenario(m_gal, i, g_width, g_height);
|
||||||
|
|
||||||
|
// Test Flush() API - flushes vertex buffer to GPU
|
||||||
|
m_gal->Flush();
|
||||||
|
|
||||||
// EndDrawing renders vertices to FBO, composites to screen, swaps buffers
|
// EndDrawing renders vertices to FBO, composites to screen, swaps buffers
|
||||||
m_gal->EndDrawing();
|
m_gal->EndDrawing();
|
||||||
m_gal->UnlockContext(i);
|
m_gal->UnlockContext(i);
|
||||||
|
|
|
||||||
|
|
@ -267,3 +267,86 @@ namespace KIFONT {
|
||||||
void FONT::Draw(KIGFX::GAL*, const wxString&, const VECTOR2I&, const VECTOR2I&,
|
void FONT::Draw(KIGFX::GAL*, const wxString&, const VECTOR2I&, const VECTOR2I&,
|
||||||
const TEXT_ATTRIBUTES&, const METRICS&) const {}
|
const TEXT_ATTRIBUTES&, const METRICS&) const {}
|
||||||
} // namespace KIFONT
|
} // namespace KIFONT
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// BITMAP_BASE stubs for DrawBitmap testing
|
||||||
|
// Only define symbols NOT already inline in bitmap_base.h
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
#include <bitmap_base.h>
|
||||||
|
|
||||||
|
// Constructor - not inline in header
|
||||||
|
BITMAP_BASE::BITMAP_BASE( const VECTOR2I& pos )
|
||||||
|
{
|
||||||
|
m_scale = 1.0;
|
||||||
|
m_imageType = wxBITMAP_TYPE_PNG;
|
||||||
|
m_bitmap = nullptr;
|
||||||
|
m_image = nullptr;
|
||||||
|
m_originalImage = nullptr;
|
||||||
|
// Use 91 PPI to match screen DPI - makes 1 bitmap pixel = 1 screen pixel
|
||||||
|
// (worldUnitLength = 1/91, so scale = 1/(91 * 1/91) = 1)
|
||||||
|
m_ppi = 91;
|
||||||
|
m_pixelSizeIu = 254000.0 / m_ppi;
|
||||||
|
m_isMirroredX = false;
|
||||||
|
m_isMirroredY = false;
|
||||||
|
m_rotation = ANGLE_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetImage - not inline in header (declared, not defined)
|
||||||
|
bool BITMAP_BASE::SetImage( const wxImage& aImage )
|
||||||
|
{
|
||||||
|
delete m_image;
|
||||||
|
delete m_originalImage;
|
||||||
|
delete m_bitmap;
|
||||||
|
|
||||||
|
m_image = new wxImage( aImage );
|
||||||
|
m_originalImage = new wxImage( aImage );
|
||||||
|
m_bitmap = new wxBitmap( *m_image );
|
||||||
|
m_imageType = wxBITMAP_TYPE_PNG;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// KIFONT::STROKE_GLYPH stubs for DrawGlyph testing
|
||||||
|
// Only define symbols NOT already inline in glyph.h
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
#include <font/glyph.h>
|
||||||
|
|
||||||
|
namespace KIFONT {
|
||||||
|
|
||||||
|
// Copy constructor - not inline
|
||||||
|
STROKE_GLYPH::STROKE_GLYPH( const STROKE_GLYPH& aGlyph )
|
||||||
|
{
|
||||||
|
reserve( aGlyph.size() );
|
||||||
|
for( const std::vector<VECTOR2D>& pointList : aGlyph )
|
||||||
|
push_back( pointList );
|
||||||
|
m_boundingBox = aGlyph.m_boundingBox;
|
||||||
|
m_penIsDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPoint - not inline
|
||||||
|
void STROKE_GLYPH::AddPoint( const VECTOR2D& aPoint )
|
||||||
|
{
|
||||||
|
if( !m_penIsDown )
|
||||||
|
{
|
||||||
|
emplace_back();
|
||||||
|
back().reserve( 16 );
|
||||||
|
m_penIsDown = true;
|
||||||
|
}
|
||||||
|
back().push_back( aPoint );
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaisePen - not inline
|
||||||
|
void STROKE_GLYPH::RaisePen()
|
||||||
|
{
|
||||||
|
m_penIsDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize - not inline
|
||||||
|
void STROKE_GLYPH::Finalize()
|
||||||
|
{
|
||||||
|
// No-op for test stub
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace KIFONT
|
||||||
|
|
|
||||||
176
tests/gal-regression/native/kifont_stub.h
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
* KIFONT Helpers for GAL Test
|
||||||
|
*
|
||||||
|
* Provides factory functions to create STROKE_GLYPH instances for testing
|
||||||
|
* DrawGlyph() and DrawGlyphs() APIs.
|
||||||
|
*
|
||||||
|
* Uses KiCad's actual KIFONT::GLYPH and KIFONT::STROKE_GLYPH classes from
|
||||||
|
* font/glyph.h. This file only provides helper functions to create test glyphs.
|
||||||
|
*
|
||||||
|
* STROKE_GLYPH inherits from std::vector<std::vector<VECTOR2D>>, where each
|
||||||
|
* inner vector represents a stroke path (pen down to pen up).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIFONT_STUB_H
|
||||||
|
#define KIFONT_STUB_H
|
||||||
|
|
||||||
|
#include <font/glyph.h>
|
||||||
|
#include <math/vector2d.h>
|
||||||
|
#include <math/box2.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace KIFONT
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create a stroke glyph from raw polyline data
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeStrokeGlyph(
|
||||||
|
const std::vector<std::vector<VECTOR2D>>& aStrokes)
|
||||||
|
{
|
||||||
|
auto glyph = std::make_unique<STROKE_GLYPH>();
|
||||||
|
|
||||||
|
for (const auto& stroke : aStrokes)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < stroke.size(); i++)
|
||||||
|
{
|
||||||
|
glyph->AddPoint(stroke[i]);
|
||||||
|
}
|
||||||
|
glyph->RaisePen();
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph->Finalize();
|
||||||
|
return glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create letter "F" as a stroke glyph
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeLetterF(double scale = 1.0, VECTOR2D offset = VECTOR2D(0, 0))
|
||||||
|
{
|
||||||
|
std::vector<std::vector<VECTOR2D>> strokes = {
|
||||||
|
// Vertical stroke
|
||||||
|
{ VECTOR2D(0, 0) * scale + offset, VECTOR2D(0, 100) * scale + offset },
|
||||||
|
// Top horizontal
|
||||||
|
{ VECTOR2D(0, 0) * scale + offset, VECTOR2D(60, 0) * scale + offset },
|
||||||
|
// Middle horizontal
|
||||||
|
{ VECTOR2D(0, 40) * scale + offset, VECTOR2D(40, 40) * scale + offset }
|
||||||
|
};
|
||||||
|
return MakeStrokeGlyph(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create letter "L" as a stroke glyph
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeLetterL(double scale = 1.0, VECTOR2D offset = VECTOR2D(0, 0))
|
||||||
|
{
|
||||||
|
std::vector<std::vector<VECTOR2D>> strokes = {
|
||||||
|
// Vertical stroke
|
||||||
|
{ VECTOR2D(0, 0) * scale + offset, VECTOR2D(0, 100) * scale + offset },
|
||||||
|
// Bottom horizontal
|
||||||
|
{ VECTOR2D(0, 100) * scale + offset, VECTOR2D(50, 100) * scale + offset }
|
||||||
|
};
|
||||||
|
return MakeStrokeGlyph(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create letter "K" as a stroke glyph
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeLetterK(double scale = 1.0, VECTOR2D offset = VECTOR2D(0, 0))
|
||||||
|
{
|
||||||
|
std::vector<std::vector<VECTOR2D>> strokes = {
|
||||||
|
// Vertical stroke
|
||||||
|
{ VECTOR2D(0, 0) * scale + offset, VECTOR2D(0, 100) * scale + offset },
|
||||||
|
// Upper diagonal
|
||||||
|
{ VECTOR2D(0, 50) * scale + offset, VECTOR2D(50, 0) * scale + offset },
|
||||||
|
// Lower diagonal
|
||||||
|
{ VECTOR2D(0, 50) * scale + offset, VECTOR2D(50, 100) * scale + offset }
|
||||||
|
};
|
||||||
|
return MakeStrokeGlyph(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create letter "I" as a stroke glyph
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeLetterI(double scale = 1.0, VECTOR2D offset = VECTOR2D(0, 0))
|
||||||
|
{
|
||||||
|
std::vector<std::vector<VECTOR2D>> strokes = {
|
||||||
|
// Top horizontal
|
||||||
|
{ VECTOR2D(-20, 0) * scale + offset, VECTOR2D(20, 0) * scale + offset },
|
||||||
|
// Vertical stroke
|
||||||
|
{ VECTOR2D(0, 0) * scale + offset, VECTOR2D(0, 100) * scale + offset },
|
||||||
|
// Bottom horizontal
|
||||||
|
{ VECTOR2D(-20, 100) * scale + offset, VECTOR2D(20, 100) * scale + offset }
|
||||||
|
};
|
||||||
|
return MakeStrokeGlyph(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create letter "C" as a stroke glyph
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeLetterC(double scale = 1.0, VECTOR2D offset = VECTOR2D(0, 0))
|
||||||
|
{
|
||||||
|
// Approximate C with connected line segments
|
||||||
|
std::vector<std::vector<VECTOR2D>> strokes = {
|
||||||
|
{
|
||||||
|
VECTOR2D(50, 10) * scale + offset,
|
||||||
|
VECTOR2D(30, 0) * scale + offset,
|
||||||
|
VECTOR2D(10, 10) * scale + offset,
|
||||||
|
VECTOR2D(0, 30) * scale + offset,
|
||||||
|
VECTOR2D(0, 70) * scale + offset,
|
||||||
|
VECTOR2D(10, 90) * scale + offset,
|
||||||
|
VECTOR2D(30, 100) * scale + offset,
|
||||||
|
VECTOR2D(50, 90) * scale + offset
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return MakeStrokeGlyph(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create letter "A" as a stroke glyph
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeLetterA(double scale = 1.0, VECTOR2D offset = VECTOR2D(0, 0))
|
||||||
|
{
|
||||||
|
std::vector<std::vector<VECTOR2D>> strokes = {
|
||||||
|
// Left diagonal
|
||||||
|
{ VECTOR2D(0, 100) * scale + offset, VECTOR2D(30, 0) * scale + offset },
|
||||||
|
// Right diagonal
|
||||||
|
{ VECTOR2D(30, 0) * scale + offset, VECTOR2D(60, 100) * scale + offset },
|
||||||
|
// Crossbar
|
||||||
|
{ VECTOR2D(15, 60) * scale + offset, VECTOR2D(45, 60) * scale + offset }
|
||||||
|
};
|
||||||
|
return MakeStrokeGlyph(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create letter "D" as a stroke glyph
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<STROKE_GLYPH> MakeLetterD(double scale = 1.0, VECTOR2D offset = VECTOR2D(0, 0))
|
||||||
|
{
|
||||||
|
std::vector<std::vector<VECTOR2D>> strokes = {
|
||||||
|
// Vertical stroke
|
||||||
|
{ VECTOR2D(0, 0) * scale + offset, VECTOR2D(0, 100) * scale + offset },
|
||||||
|
// Curved part (approximated)
|
||||||
|
{
|
||||||
|
VECTOR2D(0, 0) * scale + offset,
|
||||||
|
VECTOR2D(30, 0) * scale + offset,
|
||||||
|
VECTOR2D(50, 20) * scale + offset,
|
||||||
|
VECTOR2D(50, 80) * scale + offset,
|
||||||
|
VECTOR2D(30, 100) * scale + offset,
|
||||||
|
VECTOR2D(0, 100) * scale + offset
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return MakeStrokeGlyph(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace KIFONT
|
||||||
|
|
||||||
|
#endif // KIFONT_STUB_H
|
||||||
|
|
@ -10,6 +10,19 @@
|
||||||
* - scenario_arc_segments.cpp (12)
|
* - scenario_arc_segments.cpp (12)
|
||||||
* - scenario_segment_chain.cpp (13)
|
* - scenario_segment_chain.cpp (13)
|
||||||
* - scenario_group_caching.cpp (14)
|
* - scenario_group_caching.cpp (14)
|
||||||
|
* - scenario_polylines_multi.cpp (15)
|
||||||
|
* - scenario_hole_walls.cpp (16)
|
||||||
|
* - scenario_grid_native.cpp (17)
|
||||||
|
* - scenario_cursor_native.cpp (18)
|
||||||
|
* - scenario_render_targets.cpp (19)
|
||||||
|
* - scenario_screen_transform.cpp (20)
|
||||||
|
* - scenario_clear_colors.cpp (21)
|
||||||
|
* - scenario_depth_testing.cpp (22)
|
||||||
|
* - scenario_negative_mode.cpp (23)
|
||||||
|
* - scenario_text_attrs.cpp (24)
|
||||||
|
* - scenario_glyphs.cpp (25)
|
||||||
|
* - scenario_bitmap.cpp (26)
|
||||||
|
* - scenario_transform.cpp (27)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gal_test_scenarios.h"
|
#include "gal_test_scenarios.h"
|
||||||
|
|
@ -37,8 +50,21 @@ void RenderBezierCurves(GAL* gal, int width, int height);
|
||||||
void RenderArcSegments(GAL* gal, int width, int height);
|
void RenderArcSegments(GAL* gal, int width, int height);
|
||||||
void RenderSegmentChain(GAL* gal, int width, int height);
|
void RenderSegmentChain(GAL* gal, int width, int height);
|
||||||
void RenderGroupCaching(GAL* gal, int width, int height);
|
void RenderGroupCaching(GAL* gal, int width, int height);
|
||||||
|
void RenderPolylinesMulti(GAL* gal, int width, int height);
|
||||||
|
void RenderHoleWalls(GAL* gal, int width, int height);
|
||||||
|
void RenderGridNative(GAL* gal, int width, int height);
|
||||||
|
void RenderCursorNative(GAL* gal, int width, int height);
|
||||||
|
void RenderRenderTargets(GAL* gal, int width, int height);
|
||||||
|
void RenderScreenTransform(GAL* gal, int width, int height);
|
||||||
|
void RenderClearColors(GAL* gal, int width, int height);
|
||||||
|
void RenderDepthTesting(GAL* gal, int width, int height);
|
||||||
|
void RenderNegativeMode(GAL* gal, int width, int height);
|
||||||
|
void RenderTextAttrs(GAL* gal, int width, int height);
|
||||||
|
void RenderGlyphs(GAL* gal, int width, int height);
|
||||||
|
void RenderBitmap(GAL* gal, int width, int height);
|
||||||
|
void RenderTransformAPI(GAL* gal, int width, int height);
|
||||||
|
|
||||||
// Scenario names - original 11 + 4 new scenarios
|
// Scenario names - original 11 + 17 new scenarios
|
||||||
static const char* SCENARIO_NAMES[] = {
|
static const char* SCENARIO_NAMES[] = {
|
||||||
// Original scenarios (0-10)
|
// Original scenarios (0-10)
|
||||||
"basic-lines",
|
"basic-lines",
|
||||||
|
|
@ -52,11 +78,25 @@ static const char* SCENARIO_NAMES[] = {
|
||||||
"grid-cursor",
|
"grid-cursor",
|
||||||
"segments",
|
"segments",
|
||||||
"complex-scene",
|
"complex-scene",
|
||||||
// New scenarios (11-14) - defined in separate files
|
// New scenarios (11-23) - defined in separate files
|
||||||
"bezier-curves",
|
"bezier-curves",
|
||||||
"arc-segments",
|
"arc-segments",
|
||||||
"segment-chain",
|
"segment-chain",
|
||||||
"group-caching"
|
"group-caching",
|
||||||
|
"polylines-multi",
|
||||||
|
"hole-walls",
|
||||||
|
"grid-native",
|
||||||
|
"cursor-native",
|
||||||
|
"render-targets",
|
||||||
|
"screen-transform",
|
||||||
|
"clear-colors",
|
||||||
|
"depth-testing",
|
||||||
|
"negative-mode",
|
||||||
|
// Additional scenarios (24-27) - defined in separate files
|
||||||
|
"text-attrs",
|
||||||
|
"glyphs",
|
||||||
|
"bitmap",
|
||||||
|
"transform-api"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int SCENARIO_COUNT = sizeof(SCENARIO_NAMES) / sizeof(SCENARIO_NAMES[0]);
|
static const int SCENARIO_COUNT = sizeof(SCENARIO_NAMES) / sizeof(SCENARIO_NAMES[0]);
|
||||||
|
|
@ -111,17 +151,18 @@ static void RenderBasicLines(KIGFX::GAL* gal, int width, int height) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario 1: Line widths
|
// Scenario 1: Line widths (tests SetLineWidth and SetMinLineWidth)
|
||||||
static void RenderLineWidths(KIGFX::GAL* gal, int width, int height) {
|
static void RenderLineWidths(KIGFX::GAL* gal, int width, int height) {
|
||||||
double widths[] = {0.5, 1.0, 2.0, 3.0, 5.0, 8.0, 12.0};
|
double widths[] = {0.5, 1.0, 2.0, 3.0, 5.0, 8.0, 12.0};
|
||||||
int count = sizeof(widths) / sizeof(widths[0]);
|
int count = sizeof(widths) / sizeof(widths[0]);
|
||||||
|
|
||||||
double margin = 50.0;
|
double margin = 50.0;
|
||||||
double spacing = (height - 2 * margin) / (count + 1);
|
double spacing = (height - 2 * margin) / (count + 3); // +3 for min width demos
|
||||||
|
|
||||||
gal->SetIsFill(false);
|
gal->SetIsFill(false);
|
||||||
gal->SetIsStroke(true);
|
gal->SetIsStroke(true);
|
||||||
|
|
||||||
|
// First section: Normal line widths
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
double y = margin + (i + 1) * spacing;
|
double y = margin + (i + 1) * spacing;
|
||||||
|
|
||||||
|
|
@ -129,8 +170,35 @@ static void RenderLineWidths(KIGFX::GAL* gal, int width, int height) {
|
||||||
double t = (double)i / (count - 1);
|
double t = (double)i / (count - 1);
|
||||||
gal->SetStrokeColor(COLOR4D(1.0 - t * 0.5, 0.3 + t * 0.4, 0.2 + t * 0.6, 1.0));
|
gal->SetStrokeColor(COLOR4D(1.0 - t * 0.5, 0.3 + t * 0.4, 0.2 + t * 0.6, 1.0));
|
||||||
gal->SetLineWidth(widths[i]);
|
gal->SetLineWidth(widths[i]);
|
||||||
gal->DrawLine(VECTOR2D(margin, y), VECTOR2D(width - margin, y));
|
gal->DrawLine(VECTOR2D(margin, y), VECTOR2D(width * 0.45, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Second section: Test SetMinLineWidth
|
||||||
|
// Lines with width 0.5 but different min line widths
|
||||||
|
double baseY = margin + (count + 1) * spacing;
|
||||||
|
|
||||||
|
// Very thin line (0.1) without min width - may be invisible
|
||||||
|
gal->SetMinLineWidth(0.0); // No minimum
|
||||||
|
gal->SetLineWidth(0.1);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.3, 0.3, 1.0));
|
||||||
|
gal->DrawLine(VECTOR2D(width * 0.55, baseY), VECTOR2D(width - margin, baseY));
|
||||||
|
|
||||||
|
// Very thin line (0.1) with min width 1.0 - should be visible
|
||||||
|
baseY += spacing;
|
||||||
|
gal->SetMinLineWidth(1.0); // Minimum 1 pixel
|
||||||
|
gal->SetLineWidth(0.1);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 1.0, 0.3, 1.0));
|
||||||
|
gal->DrawLine(VECTOR2D(width * 0.55, baseY), VECTOR2D(width - margin, baseY));
|
||||||
|
|
||||||
|
// Very thin line (0.1) with min width 3.0 - should be thicker
|
||||||
|
baseY += spacing;
|
||||||
|
gal->SetMinLineWidth(3.0); // Minimum 3 pixels
|
||||||
|
gal->SetLineWidth(0.1);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.3, 1.0, 1.0));
|
||||||
|
gal->DrawLine(VECTOR2D(width * 0.55, baseY), VECTOR2D(width - margin, baseY));
|
||||||
|
|
||||||
|
// Reset min line width
|
||||||
|
gal->SetMinLineWidth(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario 2: Circles
|
// Scenario 2: Circles
|
||||||
|
|
@ -360,7 +428,8 @@ static void RenderAlphaBlending(KIGFX::GAL* gal, int width, int height) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario 7: Transforms
|
// Scenario 7: Transforms (tests Translate, Rotate, Scale, Save/Restore)
|
||||||
|
// NOTE: Transform() with MATRIX3x3D doesn't work with OPENGL_GAL's shader pipeline
|
||||||
static void RenderTransforms(KIGFX::GAL* gal, int width, int height) {
|
static void RenderTransforms(KIGFX::GAL* gal, int width, int height) {
|
||||||
double cx = width / 2.0;
|
double cx = width / 2.0;
|
||||||
double cy = height / 2.0;
|
double cy = height / 2.0;
|
||||||
|
|
@ -491,7 +560,7 @@ static void RenderSegments(KIGFX::GAL* gal, int width, int height) {
|
||||||
VECTOR2D(width / 2.0, height - margin - 150), 15.0);
|
VECTOR2D(width / 2.0, height - margin - 150), 15.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario 10: Complex scene (PCB-like)
|
// Scenario 10: Complex scene (PCB-like, tests SetLayerDepth and AdvanceDepth)
|
||||||
static void RenderComplexScene(KIGFX::GAL* gal, int width, int height) {
|
static void RenderComplexScene(KIGFX::GAL* gal, int width, int height) {
|
||||||
// Use layer depths to ensure proper z-ordering
|
// Use layer depths to ensure proper z-ordering
|
||||||
// Lower depth = closer to camera (drawn on top with GL_LESS)
|
// Lower depth = closer to camera (drawn on top with GL_LESS)
|
||||||
|
|
@ -560,6 +629,35 @@ static void RenderComplexScene(KIGFX::GAL* gal, int width, int height) {
|
||||||
gal->SetStrokeColor(COLOR4D(1.0, 1.0, 1.0, 1.0));
|
gal->SetStrokeColor(COLOR4D(1.0, 1.0, 1.0, 1.0));
|
||||||
gal->DrawRectangle(VECTOR2D(width / 2 - 20, height / 2 - 15),
|
gal->DrawRectangle(VECTOR2D(width / 2 - 20, height / 2 - 15),
|
||||||
VECTOR2D(width / 2 + 20, height / 2 + 15));
|
VECTOR2D(width / 2 + 20, height / 2 + 15));
|
||||||
|
|
||||||
|
// Demonstrate AdvanceDepth() - auto-incrementing depth
|
||||||
|
// Draw a stack of overlapping circles using AdvanceDepth
|
||||||
|
gal->SetLayerDepth(80); // Start at depth 80
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Each call to AdvanceDepth moves closer to camera (decrements depth)
|
||||||
|
double stackX = width - 120;
|
||||||
|
double stackY = height - 120;
|
||||||
|
|
||||||
|
// First circle (deepest in stack)
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.2, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(stackX, stackY), 35);
|
||||||
|
gal->AdvanceDepth(); // Move closer to camera
|
||||||
|
|
||||||
|
// Second circle
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.8, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(stackX + 15, stackY - 10), 30);
|
||||||
|
gal->AdvanceDepth();
|
||||||
|
|
||||||
|
// Third circle
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.2, 0.8, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(stackX + 30, stackY - 20), 25);
|
||||||
|
gal->AdvanceDepth();
|
||||||
|
|
||||||
|
// Fourth circle (closest to camera)
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.8, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(stackX + 45, stackY - 30), 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
|
@ -580,11 +678,25 @@ void RenderScenario(KIGFX::GAL* gal, int index, int width, int height) {
|
||||||
case 8: RenderGridCursor(gal, width, height); break;
|
case 8: RenderGridCursor(gal, width, height); break;
|
||||||
case 9: RenderSegments(gal, width, height); break;
|
case 9: RenderSegments(gal, width, height); break;
|
||||||
case 10: RenderComplexScene(gal, width, height); break;
|
case 10: RenderComplexScene(gal, width, height); break;
|
||||||
// New scenarios (11-14) - defined in separate files
|
// New scenarios (11-20) - defined in separate files
|
||||||
case 11: RenderBezierCurves(gal, width, height); break;
|
case 11: RenderBezierCurves(gal, width, height); break;
|
||||||
case 12: RenderArcSegments(gal, width, height); break;
|
case 12: RenderArcSegments(gal, width, height); break;
|
||||||
case 13: RenderSegmentChain(gal, width, height); break;
|
case 13: RenderSegmentChain(gal, width, height); break;
|
||||||
case 14: RenderGroupCaching(gal, width, height); break;
|
case 14: RenderGroupCaching(gal, width, height); break;
|
||||||
|
case 15: RenderPolylinesMulti(gal, width, height); break;
|
||||||
|
case 16: RenderHoleWalls(gal, width, height); break;
|
||||||
|
case 17: RenderGridNative(gal, width, height); break;
|
||||||
|
case 18: RenderCursorNative(gal, width, height); break;
|
||||||
|
case 19: RenderRenderTargets(gal, width, height); break;
|
||||||
|
case 20: RenderScreenTransform(gal, width, height); break;
|
||||||
|
case 21: RenderClearColors(gal, width, height); break;
|
||||||
|
case 22: RenderDepthTesting(gal, width, height); break;
|
||||||
|
case 23: RenderNegativeMode(gal, width, height); break;
|
||||||
|
// Additional scenarios (24-27) - defined in separate files
|
||||||
|
case 24: RenderTextAttrs(gal, width, height); break;
|
||||||
|
case 25: RenderGlyphs(gal, width, height); break;
|
||||||
|
case 26: RenderBitmap(gal, width, height); break;
|
||||||
|
case 27: RenderTransformAPI(gal, width, height); break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
332
tests/gal-regression/scenarios/scenario_bitmap.cpp
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
/**
|
||||||
|
* Bitmap Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL DrawBitmap() method using BITMAP_BASE with various test patterns.
|
||||||
|
*
|
||||||
|
* DrawBitmap() renders a raster image centered at the current transformation origin.
|
||||||
|
* In OPENGL_GAL, it uses GL_BITMAP_CACHE to create GPU textures from wxImage data.
|
||||||
|
* Position is controlled via Save/Translate/Restore, not by arguments to DrawBitmap.
|
||||||
|
*
|
||||||
|
* This scenario demonstrates:
|
||||||
|
* 1. Basic bitmap rendering with checkerboard pattern
|
||||||
|
* 2. Gradient patterns (horizontal, vertical, radial)
|
||||||
|
* 3. Custom KiCad logo-style pattern
|
||||||
|
* 4. Different bitmap sizes
|
||||||
|
* 5. Multiple bitmaps in a scene
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include "../native/bitmap_base_stub.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderBitmap(GAL* gal, int width, int height) {
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.1, 0.12, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(0, 0), VECTOR2D(width, height));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 1: Basic checkerboard bitmap
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
// Create checkerboard bitmap (64x64)
|
||||||
|
auto checkerboard = CreateCheckerboardBitmap(64, 64, 8);
|
||||||
|
|
||||||
|
// Position bitmap at (80, 80) - use transform
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(100, 100)); // Center position
|
||||||
|
gal->DrawBitmap(*checkerboard, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section label frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(180, 180));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 2: Gradient bitmap (horizontal)
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
auto gradient = CreateGradientBitmap(80, 60);
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(300, 100));
|
||||||
|
gal->DrawBitmap(*gradient, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.4, 0.4, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(200, 20), VECTOR2D(400, 180));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 3: KiCad logo pattern
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
auto logo = CreateKiCadLogoBitmap(80, 80);
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(520, 100));
|
||||||
|
gal->DrawBitmap(*logo, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.5, 0.3, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(420, 20), VECTOR2D(620, 180));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 4: Radial gradient
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
auto radial = CreateRadialBitmap(64, 64);
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(720, 100));
|
||||||
|
gal->DrawBitmap(*radial, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(640, 20), VECTOR2D(800, 180));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 5: Different bitmap sizes
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 200), VECTOR2D(380, 380));
|
||||||
|
|
||||||
|
// Small bitmap (32x32)
|
||||||
|
auto small = CreateCheckerboardBitmap(32, 32, 4);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(80, 280));
|
||||||
|
gal->DrawBitmap(*small, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Medium bitmap (64x64)
|
||||||
|
auto medium = CreateCheckerboardBitmap(64, 64, 8);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(180, 290));
|
||||||
|
gal->DrawBitmap(*medium, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Large bitmap (96x96)
|
||||||
|
auto large = CreateCheckerboardBitmap(96, 96, 12);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(300, 290));
|
||||||
|
gal->DrawBitmap(*large, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.4, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 200), VECTOR2D(380, 380));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 6: Striped patterns
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.12, 0.12, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 200), VECTOR2D(600, 380));
|
||||||
|
|
||||||
|
// Horizontal stripes
|
||||||
|
auto hStripes = CreateStripedBitmap(64, 64, true);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(460, 290));
|
||||||
|
gal->DrawBitmap(*hStripes, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Vertical stripes
|
||||||
|
auto vStripes = CreateStripedBitmap(64, 64, false);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(550, 290));
|
||||||
|
gal->DrawBitmap(*vStripes, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.4, 0.4, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 200), VECTOR2D(600, 380));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 7: Multiple bitmaps composition - colored checkerboards
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.15, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(620, 200), VECTOR2D(800, 380));
|
||||||
|
|
||||||
|
// Create a variety of colored checkerboards in a grid
|
||||||
|
wxImage img1 = CreateCheckerboardImage(40, 40, 5, 255, 200, 200, 100, 50, 50);
|
||||||
|
wxImage img2 = CreateCheckerboardImage(40, 40, 5, 200, 255, 200, 50, 100, 50);
|
||||||
|
wxImage img3 = CreateCheckerboardImage(40, 40, 5, 200, 200, 255, 50, 50, 100);
|
||||||
|
wxImage img4 = CreateCheckerboardImage(40, 40, 5, 255, 255, 200, 100, 100, 50);
|
||||||
|
|
||||||
|
auto bmp1 = std::make_unique<BITMAP_BASE>(); bmp1->SetImage(img1);
|
||||||
|
auto bmp2 = std::make_unique<BITMAP_BASE>(); bmp2->SetImage(img2);
|
||||||
|
auto bmp3 = std::make_unique<BITMAP_BASE>(); bmp3->SetImage(img3);
|
||||||
|
auto bmp4 = std::make_unique<BITMAP_BASE>(); bmp4->SetImage(img4);
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(670, 260));
|
||||||
|
gal->DrawBitmap(*bmp1, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(750, 260));
|
||||||
|
gal->DrawBitmap(*bmp2, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(670, 330));
|
||||||
|
gal->DrawBitmap(*bmp3, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(750, 330));
|
||||||
|
gal->DrawBitmap(*bmp4, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.5, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(620, 200), VECTOR2D(800, 380));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 8: Bitmap with surrounding graphics
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 400), VECTOR2D(400, 580));
|
||||||
|
|
||||||
|
// Central bitmap
|
||||||
|
auto central = CreateKiCadLogoBitmap(100, 100);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(210, 490));
|
||||||
|
gal->DrawBitmap(*central, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Decorative circles around bitmap
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
|
||||||
|
COLOR4D circleColors[] = {
|
||||||
|
COLOR4D(0.9, 0.4, 0.4, 0.7),
|
||||||
|
COLOR4D(0.4, 0.9, 0.4, 0.7),
|
||||||
|
COLOR4D(0.4, 0.4, 0.9, 0.7),
|
||||||
|
COLOR4D(0.9, 0.9, 0.4, 0.7)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
gal->SetStrokeColor(circleColors[i]);
|
||||||
|
double angle = i * M_PI / 2;
|
||||||
|
double cx = 210 + cos(angle) * 80;
|
||||||
|
double cy = 490 + sin(angle) * 80;
|
||||||
|
gal->DrawCircle(VECTOR2D(cx, cy), 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connecting lines
|
||||||
|
gal->SetLineWidth(1.5);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.7, 0.5));
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
double angle1 = i * M_PI / 2;
|
||||||
|
double angle2 = ((i + 1) % 4) * M_PI / 2;
|
||||||
|
VECTOR2D p1(210 + cos(angle1) * 80, 490 + sin(angle1) * 80);
|
||||||
|
VECTOR2D p2(210 + cos(angle2) * 80, 490 + sin(angle2) * 80);
|
||||||
|
gal->DrawLine(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 400), VECTOR2D(400, 580));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 9: Gradient showcase
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.1, 0.12, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(420, 400), VECTOR2D(800, 580));
|
||||||
|
|
||||||
|
// Horizontal gradient
|
||||||
|
wxImage hGradImg = CreateGradientHImage(100, 40, 255, 100, 100, 100, 100, 255);
|
||||||
|
auto hGradBitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
hGradBitmap->SetImage(hGradImg);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(520, 450));
|
||||||
|
gal->DrawBitmap(*hGradBitmap, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Vertical gradient
|
||||||
|
wxImage vGradImg = CreateGradientVImage(100, 40, 100, 255, 100, 100, 100, 255);
|
||||||
|
auto vGradBitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
vGradBitmap->SetImage(vGradImg);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(520, 510));
|
||||||
|
gal->DrawBitmap(*vGradBitmap, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Radial gradient (larger)
|
||||||
|
wxImage radialImg = CreateRadialGradientImage(80, 80, 255, 255, 100, 100, 50, 150);
|
||||||
|
auto radialBitmap = std::make_unique<BITMAP_BASE>();
|
||||||
|
radialBitmap->SetImage(radialImg);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(700, 490));
|
||||||
|
gal->DrawBitmap(*radialBitmap, 1.0);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.4, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(420, 400), VECTOR2D(800, 580));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
169
tests/gal-regression/scenarios/scenario_clear_colors.cpp
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
/**
|
||||||
|
* Clear Colors Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL::SetClearColor() - background color setting
|
||||||
|
*
|
||||||
|
* Note: SetClearColor affects the background when ClearScreen is called.
|
||||||
|
* Since we can only have one background per frame, this demonstrates
|
||||||
|
* the concept by drawing colored rectangles to show different clear colors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderClearColors(GAL* gal, int width, int height) {
|
||||||
|
// Demonstrate SetClearColor by showing what different backgrounds look like
|
||||||
|
// We simulate this with filled rectangles since we can only clear once per frame
|
||||||
|
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
|
||||||
|
double boxW = 180;
|
||||||
|
double boxH = 120;
|
||||||
|
double margin = 20;
|
||||||
|
|
||||||
|
// Row 1: Standard backgrounds
|
||||||
|
// Dark theme
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.1, 0.12, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin, margin), VECTOR2D(margin + boxW, margin + boxH));
|
||||||
|
|
||||||
|
// Light theme
|
||||||
|
gal->SetFillColor(COLOR4D(0.95, 0.95, 0.95, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + boxW + 20, margin),
|
||||||
|
VECTOR2D(margin + boxW * 2 + 20, margin + boxH));
|
||||||
|
|
||||||
|
// Blue-gray
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.18, 0.22, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + (boxW + 20) * 2, margin),
|
||||||
|
VECTOR2D(margin + boxW * 3 + 40, margin + boxH));
|
||||||
|
|
||||||
|
// Green tint (PCB style)
|
||||||
|
gal->SetFillColor(COLOR4D(0.08, 0.15, 0.08, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + (boxW + 20) * 3, margin),
|
||||||
|
VECTOR2D(margin + boxW * 4 + 60, margin + boxH));
|
||||||
|
|
||||||
|
// Row 2: More color options
|
||||||
|
double row2Y = margin + boxH + 30;
|
||||||
|
|
||||||
|
// Warm gray
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.18, 0.16, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin, row2Y), VECTOR2D(margin + boxW, row2Y + boxH));
|
||||||
|
|
||||||
|
// Deep blue
|
||||||
|
gal->SetFillColor(COLOR4D(0.05, 0.08, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + boxW + 20, row2Y),
|
||||||
|
VECTOR2D(margin + boxW * 2 + 20, row2Y + boxH));
|
||||||
|
|
||||||
|
// Pure white
|
||||||
|
gal->SetFillColor(COLOR4D(1.0, 1.0, 1.0, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + (boxW + 20) * 2, row2Y),
|
||||||
|
VECTOR2D(margin + boxW * 3 + 40, row2Y + boxH));
|
||||||
|
|
||||||
|
// Pure black
|
||||||
|
gal->SetFillColor(COLOR4D(0.0, 0.0, 0.0, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + (boxW + 20) * 3, row2Y),
|
||||||
|
VECTOR2D(margin + boxW * 4 + 60, row2Y + boxH));
|
||||||
|
|
||||||
|
// Row 3: Demonstrate content on different backgrounds
|
||||||
|
double row3Y = row2Y + boxH + 30;
|
||||||
|
|
||||||
|
// Dark background with light content
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.1, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin, row3Y), VECTOR2D(margin + boxW, row3Y + boxH));
|
||||||
|
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.2, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(margin + boxW/2 - 30, row3Y + boxH/2), 20);
|
||||||
|
gal->DrawCircle(VECTOR2D(margin + boxW/2 + 30, row3Y + boxH/2), 20);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.9, 0.9, 0.2, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + 30, row3Y + 30), VECTOR2D(margin + boxW - 30, row3Y + boxH - 30));
|
||||||
|
|
||||||
|
// Light background with dark content
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.95, 0.95, 0.92, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + boxW + 20, row3Y),
|
||||||
|
VECTOR2D(margin + boxW * 2 + 20, row3Y + boxH));
|
||||||
|
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.2, 0.3, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(margin + boxW * 1.5 + 20 - 30, row3Y + boxH/2), 20);
|
||||||
|
gal->DrawCircle(VECTOR2D(margin + boxW * 1.5 + 20 + 30, row3Y + boxH/2), 20);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.1, 0.1, 0.2, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + boxW + 50, row3Y + 30),
|
||||||
|
VECTOR2D(margin + boxW * 2 - 10, row3Y + boxH - 30));
|
||||||
|
|
||||||
|
// PCB green background
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.05, 0.2, 0.05, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + (boxW + 20) * 2, row3Y),
|
||||||
|
VECTOR2D(margin + boxW * 3 + 40, row3Y + boxH));
|
||||||
|
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.2, 1.0));
|
||||||
|
double pcbX = margin + (boxW + 20) * 2 + boxW/2;
|
||||||
|
gal->DrawSegment(VECTOR2D(pcbX - 50, row3Y + 40), VECTOR2D(pcbX + 50, row3Y + 40), 6);
|
||||||
|
gal->DrawSegment(VECTOR2D(pcbX - 50, row3Y + boxH - 40), VECTOR2D(pcbX + 50, row3Y + boxH - 40), 6);
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.7, 0.3, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(pcbX - 30, row3Y + 40), 10);
|
||||||
|
gal->DrawCircle(VECTOR2D(pcbX + 30, row3Y + 40), 10);
|
||||||
|
gal->DrawCircle(VECTOR2D(pcbX - 30, row3Y + boxH - 40), 10);
|
||||||
|
gal->DrawCircle(VECTOR2D(pcbX + 30, row3Y + boxH - 40), 10);
|
||||||
|
|
||||||
|
// Blue schematic background
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.95, 1.0, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin + (boxW + 20) * 3, row3Y),
|
||||||
|
VECTOR2D(margin + boxW * 4 + 60, row3Y + boxH));
|
||||||
|
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.0, 0.4, 0.0, 1.0));
|
||||||
|
double schX = margin + (boxW + 20) * 3 + boxW/2;
|
||||||
|
gal->DrawLine(VECTOR2D(schX - 60, row3Y + boxH/2), VECTOR2D(schX - 20, row3Y + boxH/2));
|
||||||
|
gal->DrawLine(VECTOR2D(schX + 20, row3Y + boxH/2), VECTOR2D(schX + 60, row3Y + boxH/2));
|
||||||
|
gal->DrawRectangle(VECTOR2D(schX - 20, row3Y + boxH/2 - 25), VECTOR2D(schX + 20, row3Y + boxH/2 + 25));
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.0, 0.0, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(schX - 60, row3Y + boxH/2), 5);
|
||||||
|
gal->DrawCircle(VECTOR2D(schX + 60, row3Y + boxH/2), 5);
|
||||||
|
|
||||||
|
// Row 4: Test actual SetClearColor API (affects next frame)
|
||||||
|
double row4Y = row3Y + boxH + 30;
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Show the API being called (visual marker)
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.2, 0.25, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin, row4Y), VECTOR2D(width - margin, row4Y + 60));
|
||||||
|
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(margin, row4Y), VECTOR2D(width - margin, row4Y + 60));
|
||||||
|
|
||||||
|
// Demonstrate SetClearColor API call
|
||||||
|
COLOR4D clearColor(0.1, 0.1, 0.15, 1.0);
|
||||||
|
gal->SetClearColor(clearColor);
|
||||||
|
// Note: The clear color will be used on next ClearScreen() call
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
173
tests/gal-regression/scenarios/scenario_cursor_native.cpp
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
/**
|
||||||
|
* Cursor Native Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL cursor-related methods:
|
||||||
|
* - SetCursorEnabled() / IsCursorEnabled()
|
||||||
|
* - SetCursorColor()
|
||||||
|
* - DrawCursor()
|
||||||
|
*
|
||||||
|
* The cursor is typically a crosshair drawn at a specific location.
|
||||||
|
* This scenario demonstrates different cursor styles and colors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderCursorNative(GAL* gal, int width, int height) {
|
||||||
|
// Background
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(0, 0), VECTOR2D(width, height));
|
||||||
|
|
||||||
|
// Draw a grid to give context for cursor positions
|
||||||
|
gal->SetLayerDepth(90);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.25, 0.25, 0.3, 0.5));
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x += 40) {
|
||||||
|
gal->DrawLine(VECTOR2D(x, 0), VECTOR2D(x, height));
|
||||||
|
}
|
||||||
|
for (int y = 0; y < height; y += 40) {
|
||||||
|
gal->DrawLine(VECTOR2D(0, y), VECTOR2D(width, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Default cursor (white)
|
||||||
|
gal->SetLayerDepth(10);
|
||||||
|
gal->SetCursorEnabled(true);
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 1.0, 1.0, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(100, 100));
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.6, 1.0));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(60, 60), VECTOR2D(140, 70));
|
||||||
|
|
||||||
|
// Test 2: Red cursor
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 0.3, 0.3, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(250, 100));
|
||||||
|
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.6, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(210, 60), VECTOR2D(290, 70));
|
||||||
|
|
||||||
|
// Test 3: Green cursor
|
||||||
|
gal->SetCursorColor(COLOR4D(0.3, 1.0, 0.3, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(400, 100));
|
||||||
|
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.6, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(360, 60), VECTOR2D(440, 70));
|
||||||
|
|
||||||
|
// Test 4: Blue cursor
|
||||||
|
gal->SetCursorColor(COLOR4D(0.3, 0.3, 1.0, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(550, 100));
|
||||||
|
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.6, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(510, 60), VECTOR2D(590, 70));
|
||||||
|
|
||||||
|
// Test 5: Yellow cursor (selection color)
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 1.0, 0.2, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(700, 100));
|
||||||
|
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.6, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(660, 60), VECTOR2D(740, 70));
|
||||||
|
|
||||||
|
// Test 6: Semi-transparent cursors
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 1.0, 1.0, 0.3));
|
||||||
|
gal->DrawCursor(VECTOR2D(100, 250));
|
||||||
|
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 1.0, 1.0, 0.5));
|
||||||
|
gal->DrawCursor(VECTOR2D(200, 250));
|
||||||
|
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 1.0, 1.0, 0.7));
|
||||||
|
gal->DrawCursor(VECTOR2D(300, 250));
|
||||||
|
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 1.0, 1.0, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(400, 250));
|
||||||
|
|
||||||
|
// Label for alpha row
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.6, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(60, 210), VECTOR2D(440, 220));
|
||||||
|
|
||||||
|
// Test 7: Cursor positions along a path
|
||||||
|
gal->SetCursorColor(COLOR4D(0.8, 0.5, 0.2, 1.0));
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
double t = (double)i / 7.0;
|
||||||
|
double x = 100 + t * 600;
|
||||||
|
double y = 380 + sin(t * M_PI * 2) * 50;
|
||||||
|
gal->DrawCursor(VECTOR2D(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the path itself
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.4, 0.5));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
std::vector<VECTOR2D> path;
|
||||||
|
for (int i = 0; i <= 50; i++) {
|
||||||
|
double t = (double)i / 50.0;
|
||||||
|
double x = 100 + t * 600;
|
||||||
|
double y = 380 + sin(t * M_PI * 2) * 50;
|
||||||
|
path.push_back(VECTOR2D(x, y));
|
||||||
|
}
|
||||||
|
gal->DrawPolyline(path);
|
||||||
|
|
||||||
|
// Test 8: Cursor with different context - on objects
|
||||||
|
// Draw some objects
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Pad
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.2, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(600, 250), 30);
|
||||||
|
|
||||||
|
// Trace
|
||||||
|
gal->DrawSegment(VECTOR2D(550, 250), VECTOR2D(650, 250), 8);
|
||||||
|
|
||||||
|
// Cursor on the pad
|
||||||
|
gal->SetLayerDepth(5);
|
||||||
|
gal->SetCursorColor(COLOR4D(1.0, 1.0, 1.0, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(600, 250));
|
||||||
|
|
||||||
|
// Test 9: Multiple cursors showing cursor enabled/disabled
|
||||||
|
gal->SetLayerDepth(5);
|
||||||
|
|
||||||
|
// Enabled cursor
|
||||||
|
gal->SetCursorEnabled(true);
|
||||||
|
gal->SetCursorColor(COLOR4D(0.3, 1.0, 0.3, 1.0));
|
||||||
|
gal->DrawCursor(VECTOR2D(700, 350));
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.8, 0.3, 0.8));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(660, 310), VECTOR2D(740, 320));
|
||||||
|
|
||||||
|
// "Disabled" cursor (we still draw it but with different color to show the state)
|
||||||
|
// Note: SetCursorEnabled(false) would prevent DrawCursor from rendering
|
||||||
|
// So we show it as a dimmed cursor instead
|
||||||
|
gal->SetCursorColor(COLOR4D(0.5, 0.5, 0.5, 0.3));
|
||||||
|
gal->DrawCursor(VECTOR2D(700, 450));
|
||||||
|
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.5, 0.5));
|
||||||
|
gal->DrawRectangle(VECTOR2D(660, 410), VECTOR2D(740, 420));
|
||||||
|
|
||||||
|
// Border around entire test area
|
||||||
|
gal->SetLayerDepth(1);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.4, 1.0));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(10, 10), VECTOR2D(width - 10, height - 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
200
tests/gal-regression/scenarios/scenario_depth_testing.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
/**
|
||||||
|
* Depth Testing Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL::EnableDepthTest() - explicit depth test control
|
||||||
|
*
|
||||||
|
* Depth testing determines whether fragments are drawn based on their
|
||||||
|
* depth value. When enabled with GL_LESS, closer fragments overwrite
|
||||||
|
* farther ones. This test demonstrates depth ordering with overlapping shapes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderDepthTesting(GAL* gal, int width, int height) {
|
||||||
|
// Enable depth testing
|
||||||
|
gal->EnableDepthTest(true);
|
||||||
|
|
||||||
|
// Test SetDepthRange() API - sets the near/far depth range for rendering
|
||||||
|
// x = near, y = far - this maps layer depths to NDC z-values
|
||||||
|
// Default is typically VECTOR2D(0.1, 100) for KiCad
|
||||||
|
gal->SetDepthRange(VECTOR2D(0.1, 100));
|
||||||
|
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Section 1: Overlapping circles with explicit depth ordering
|
||||||
|
// Using SetLayerDepth - lower values are closer to camera
|
||||||
|
|
||||||
|
// Background reference
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.18, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(280, 200));
|
||||||
|
|
||||||
|
// Draw circles from back to front
|
||||||
|
gal->SetLayerDepth(80);
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.2, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(100, 100), 50);
|
||||||
|
|
||||||
|
gal->SetLayerDepth(60);
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.8, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(140, 110), 50);
|
||||||
|
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.2, 0.8, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(180, 100), 50);
|
||||||
|
|
||||||
|
gal->SetLayerDepth(20);
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.8, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(220, 90), 50);
|
||||||
|
|
||||||
|
// Section 2: Depth ordering with rectangles
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.18, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(300, 20), VECTOR2D(560, 200));
|
||||||
|
|
||||||
|
// Stack of rectangles
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
double t = (double)i / 4.0;
|
||||||
|
gal->SetLayerDepth(90 - i * 15);
|
||||||
|
gal->SetFillColor(COLOR4D(0.3 + t * 0.5, 0.3 + t * 0.2, 0.8 - t * 0.3, 0.9));
|
||||||
|
gal->DrawRectangle(
|
||||||
|
VECTOR2D(320 + i * 25, 40 + i * 20),
|
||||||
|
VECTOR2D(420 + i * 25, 120 + i * 20)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 3: Complex depth scene (PCB-like)
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.2, 0.1, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(580, 20), VECTOR2D(780, 200));
|
||||||
|
|
||||||
|
// Traces at depth 70
|
||||||
|
gal->SetLayerDepth(70);
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.5, 0.2, 1.0));
|
||||||
|
gal->DrawSegment(VECTOR2D(600, 60), VECTOR2D(760, 60), 8);
|
||||||
|
gal->DrawSegment(VECTOR2D(600, 110), VECTOR2D(760, 110), 8);
|
||||||
|
gal->DrawSegment(VECTOR2D(600, 160), VECTOR2D(760, 160), 8);
|
||||||
|
|
||||||
|
// Pads at depth 50 (above traces)
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetFillColor(COLOR4D(0.85, 0.65, 0.25, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(620, 60), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 60), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(740, 60), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(620, 110), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 110), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(740, 110), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(620, 160), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 160), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(740, 160), 15);
|
||||||
|
|
||||||
|
// Holes at depth 30 (above pads)
|
||||||
|
gal->SetLayerDepth(30);
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.1, 0.1, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(620, 60), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 60), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(740, 60), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(620, 110), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 110), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(740, 110), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(620, 160), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 160), 6);
|
||||||
|
gal->DrawCircle(VECTOR2D(740, 160), 6);
|
||||||
|
|
||||||
|
// Section 4: Interleaved depths
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.18, 0.15, 0.18, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 220), VECTOR2D(280, 400));
|
||||||
|
|
||||||
|
// Create a checkerboard-like depth pattern
|
||||||
|
for (int row = 0; row < 3; row++) {
|
||||||
|
for (int col = 0; col < 3; col++) {
|
||||||
|
int depth = ((row + col) % 2 == 0) ? 60 : 40;
|
||||||
|
double t = (row * 3 + col) / 8.0;
|
||||||
|
gal->SetLayerDepth(depth);
|
||||||
|
gal->SetFillColor(COLOR4D(0.8 * t + 0.2, 0.3 + 0.5 * (1 - t), 0.5, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(70 + col * 70, 270 + row * 50), 25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 5: AdvanceDepth demonstration
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.18, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(300, 220), VECTOR2D(560, 400));
|
||||||
|
|
||||||
|
// Use AdvanceDepth to automatically increment depth
|
||||||
|
gal->SetLayerDepth(90);
|
||||||
|
double cx = 430;
|
||||||
|
double cy = 310;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
double angle = i * M_PI / 4;
|
||||||
|
double r = 60;
|
||||||
|
double x = cx + cos(angle) * r;
|
||||||
|
double y = cy + sin(angle) * r;
|
||||||
|
|
||||||
|
double t = (double)i / 7.0;
|
||||||
|
gal->SetFillColor(COLOR4D(1.0 - t * 0.5, 0.3 + t * 0.4, 0.3 + t * 0.5, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(x, y), 30);
|
||||||
|
gal->AdvanceDepth(); // Each subsequent circle is closer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center circle (closest)
|
||||||
|
gal->SetFillColor(COLOR4D(1.0, 1.0, 1.0, 0.95));
|
||||||
|
gal->DrawCircle(VECTOR2D(cx, cy), 25);
|
||||||
|
|
||||||
|
// Section 6: Depth test off comparison
|
||||||
|
// This section shows what happens without proper depth ordering
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.18, 0.18, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(580, 220), VECTOR2D(780, 400));
|
||||||
|
|
||||||
|
// All at same depth - draw order determines visibility
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.2, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(640, 300), 40);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.8, 0.2, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 310), 40);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.2, 0.8, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(720, 300), 40);
|
||||||
|
|
||||||
|
// Labels/frames
|
||||||
|
gal->SetLayerDepth(10);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(280, 200));
|
||||||
|
gal->DrawRectangle(VECTOR2D(300, 20), VECTOR2D(560, 200));
|
||||||
|
gal->DrawRectangle(VECTOR2D(580, 20), VECTOR2D(780, 200));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 220), VECTOR2D(280, 400));
|
||||||
|
gal->DrawRectangle(VECTOR2D(300, 220), VECTOR2D(560, 400));
|
||||||
|
gal->DrawRectangle(VECTOR2D(580, 220), VECTOR2D(780, 400));
|
||||||
|
|
||||||
|
// Bottom info bar
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 420), VECTOR2D(780, 480));
|
||||||
|
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.3, 0.4, 0.8));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 420), VECTOR2D(780, 480));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
245
tests/gal-regression/scenarios/scenario_glyphs.cpp
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
/**
|
||||||
|
* Glyphs Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL DrawGlyph() and DrawGlyphs() methods using STROKE_GLYPH.
|
||||||
|
*
|
||||||
|
* STROKE_GLYPH inherits from std::vector<std::vector<VECTOR2D>>, where each
|
||||||
|
* inner vector is a stroke path (pen down to pen up). DrawGlyph() for stroke
|
||||||
|
* glyphs internally calls DrawPolylines() to render the strokes.
|
||||||
|
*
|
||||||
|
* This scenario demonstrates:
|
||||||
|
* 1. Single glyph rendering with DrawGlyph()
|
||||||
|
* 2. Multiple glyph rendering with DrawGlyphs()
|
||||||
|
* 3. Different glyph sizes and positions
|
||||||
|
* 4. "KICAD" text spelling using stroke glyphs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include "../native/kifont_stub.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderGlyphs(GAL* gal, int width, int height) {
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(0, 0), VECTOR2D(width, height));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 1: Single DrawGlyph() calls
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
|
||||||
|
// Letter F
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.9, 0.3, 0.3, 1.0));
|
||||||
|
auto glyphF = KIFONT::MakeLetterF(0.8, VECTOR2D(50, 50));
|
||||||
|
gal->DrawGlyph(*glyphF, 0, 1);
|
||||||
|
|
||||||
|
// Letter L
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.9, 0.3, 1.0));
|
||||||
|
auto glyphL = KIFONT::MakeLetterL(0.8, VECTOR2D(130, 50));
|
||||||
|
gal->DrawGlyph(*glyphL, 0, 1);
|
||||||
|
|
||||||
|
// Letter K
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.3, 0.9, 1.0));
|
||||||
|
auto glyphK = KIFONT::MakeLetterK(0.8, VECTOR2D(200, 50));
|
||||||
|
gal->DrawGlyph(*glyphK, 0, 1);
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.4, 0.4, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(280, 180));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 2: DrawGlyphs() with vector of glyphs
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.9, 0.7, 0.2, 1.0));
|
||||||
|
|
||||||
|
// Create "KICAD" using stroke glyphs
|
||||||
|
std::vector<std::unique_ptr<KIFONT::GLYPH>> kicadGlyphs;
|
||||||
|
|
||||||
|
kicadGlyphs.push_back(KIFONT::MakeLetterK(0.7, VECTOR2D(320, 60)));
|
||||||
|
kicadGlyphs.push_back(KIFONT::MakeLetterI(0.7, VECTOR2D(390, 60)));
|
||||||
|
kicadGlyphs.push_back(KIFONT::MakeLetterC(0.7, VECTOR2D(440, 60)));
|
||||||
|
kicadGlyphs.push_back(KIFONT::MakeLetterA(0.7, VECTOR2D(510, 60)));
|
||||||
|
kicadGlyphs.push_back(KIFONT::MakeLetterD(0.7, VECTOR2D(590, 60)));
|
||||||
|
|
||||||
|
// Draw all glyphs at once
|
||||||
|
gal->DrawGlyphs(kicadGlyphs);
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.5, 0.2, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(300, 20), VECTOR2D(680, 180));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 3: Different sizes demonstration
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
|
||||||
|
double scales[] = {0.3, 0.5, 0.8, 1.2};
|
||||||
|
COLOR4D colors[] = {
|
||||||
|
COLOR4D(0.6, 0.8, 0.9, 1.0),
|
||||||
|
COLOR4D(0.7, 0.9, 0.8, 1.0),
|
||||||
|
COLOR4D(0.9, 0.8, 0.7, 1.0),
|
||||||
|
COLOR4D(0.9, 0.7, 0.8, 1.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
double xPos = 50;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
gal->SetStrokeColor(colors[i]);
|
||||||
|
gal->SetLineWidth(1.5 + i * 0.5);
|
||||||
|
|
||||||
|
auto glyph = KIFONT::MakeLetterF(scales[i], VECTOR2D(xPos, 220));
|
||||||
|
gal->DrawGlyph(*glyph, i, 4); // Pass aNth and aTotal
|
||||||
|
|
||||||
|
xPos += 60 * scales[i] + 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.6, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 200), VECTOR2D(380, 360));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 4: Complex glyph composition
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.18, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 200), VECTOR2D(760, 360));
|
||||||
|
|
||||||
|
// Draw multiple letters in a grid pattern
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<KIFONT::GLYPH>> gridGlyphs;
|
||||||
|
|
||||||
|
// Row 1: FLKA
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.7, 0.9, 0.6, 1.0));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterF(0.5, VECTOR2D(420, 220)));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterL(0.5, VECTOR2D(480, 220)));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterK(0.5, VECTOR2D(540, 220)));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterA(0.5, VECTOR2D(600, 220)));
|
||||||
|
|
||||||
|
gal->DrawGlyphs(gridGlyphs);
|
||||||
|
gridGlyphs.clear();
|
||||||
|
|
||||||
|
// Row 2: ICDA
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.7, 0.9, 1.0));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterI(0.5, VECTOR2D(420, 290)));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterC(0.5, VECTOR2D(480, 290)));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterD(0.5, VECTOR2D(540, 290)));
|
||||||
|
gridGlyphs.push_back(KIFONT::MakeLetterA(0.5, VECTOR2D(600, 290)));
|
||||||
|
|
||||||
|
gal->DrawGlyphs(gridGlyphs);
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.6, 0.4, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 200), VECTOR2D(760, 360));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 5: Styled text with transforms
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.18, 0.15, 0.18, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 380), VECTOR2D(380, 560));
|
||||||
|
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
|
||||||
|
// Rotated letters
|
||||||
|
double angles[] = {-15, 0, 15, 30};
|
||||||
|
double xPositions[] = {60, 130, 200, 270};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(xPositions[i] + 30, 470));
|
||||||
|
gal->Rotate(angles[i] * M_PI / 180.0);
|
||||||
|
|
||||||
|
double t = (double)i / 3.0;
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.9 - t * 0.3, 0.4 + t * 0.4, 0.6 + t * 0.3, 1.0));
|
||||||
|
|
||||||
|
auto glyph = KIFONT::MakeLetterF(0.6, VECTOR2D(-20, -40));
|
||||||
|
gal->DrawGlyph(*glyph, i, 4);
|
||||||
|
|
||||||
|
gal->Restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.4, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 380), VECTOR2D(380, 560));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 6: Full "KICAD" banner
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.15, 0.2, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 380), VECTOR2D(760, 560));
|
||||||
|
|
||||||
|
// Large KICAD text
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(4.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.85, 0.3, 1.0));
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<KIFONT::GLYPH>> bannerGlyphs;
|
||||||
|
double bannerScale = 1.0;
|
||||||
|
double bannerX = 420;
|
||||||
|
double bannerY = 420;
|
||||||
|
double spacing = 70;
|
||||||
|
|
||||||
|
bannerGlyphs.push_back(KIFONT::MakeLetterK(bannerScale, VECTOR2D(bannerX, bannerY)));
|
||||||
|
bannerGlyphs.push_back(KIFONT::MakeLetterI(bannerScale, VECTOR2D(bannerX + spacing, bannerY)));
|
||||||
|
bannerGlyphs.push_back(KIFONT::MakeLetterC(bannerScale, VECTOR2D(bannerX + spacing * 2, bannerY)));
|
||||||
|
bannerGlyphs.push_back(KIFONT::MakeLetterA(bannerScale, VECTOR2D(bannerX + spacing * 3, bannerY)));
|
||||||
|
bannerGlyphs.push_back(KIFONT::MakeLetterD(bannerScale, VECTOR2D(bannerX + spacing * 4, bannerY)));
|
||||||
|
|
||||||
|
gal->DrawGlyphs(bannerGlyphs);
|
||||||
|
|
||||||
|
// Underline decoration
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.6, 0.2, 0.8));
|
||||||
|
gal->DrawLine(VECTOR2D(bannerX, bannerY + 110), VECTOR2D(bannerX + spacing * 4 + 50, bannerY + 110));
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.3, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 380), VECTOR2D(760, 560));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
214
tests/gal-regression/scenarios/scenario_grid_native.cpp
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
/**
|
||||||
|
* Grid Native Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL grid-related methods:
|
||||||
|
* - SetGridVisibility() / GetGridVisibility()
|
||||||
|
* - SetGridOrigin()
|
||||||
|
* - SetGridSize()
|
||||||
|
* - SetGridColor()
|
||||||
|
* - SetAxesEnabled() / SetAxesColor()
|
||||||
|
* - SetCoarseGrid()
|
||||||
|
* - DrawGrid()
|
||||||
|
*
|
||||||
|
* Note: The grid system in GAL is designed for the viewport,
|
||||||
|
* so we demonstrate grid properties through explicit DrawGrid() calls
|
||||||
|
* with different settings applied to different regions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderGridNative(GAL* gal, int width, int height) {
|
||||||
|
// First, let's demonstrate the grid API by setting up and drawing
|
||||||
|
// different grid configurations
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Test GetGridPoint() API - snaps a world point to nearest grid point
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetGridSize(VECTOR2D(20, 20));
|
||||||
|
gal->SetGridOrigin(VECTOR2D(0, 0));
|
||||||
|
|
||||||
|
// Test GetGridPoint - should snap (55, 47) to nearest grid point (60, 40)
|
||||||
|
VECTOR2D testPoint(55, 47);
|
||||||
|
VECTOR2D snappedPoint = gal->GetGridPoint(testPoint);
|
||||||
|
// snappedPoint should now be (60, 40) or similar based on grid settings
|
||||||
|
|
||||||
|
// Default background
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(0, 0), VECTOR2D(width, height));
|
||||||
|
|
||||||
|
// Region 1: Fine grid with axes
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetGridVisibility(true);
|
||||||
|
gal->SetGridOrigin(VECTOR2D(100, 100));
|
||||||
|
gal->SetGridSize(VECTOR2D(20, 20)); // 20-pixel grid
|
||||||
|
gal->SetGridColor(COLOR4D(0.3, 0.3, 0.5, 0.5));
|
||||||
|
gal->SetAxesEnabled(true);
|
||||||
|
gal->SetAxesColor(COLOR4D(0.8, 0.3, 0.3, 0.8));
|
||||||
|
gal->SetCoarseGrid(5); // Every 5th line is coarse
|
||||||
|
|
||||||
|
// Draw the grid (this uses the internal grid renderer)
|
||||||
|
gal->DrawGrid();
|
||||||
|
|
||||||
|
// Mark region 1 boundary
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.8, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(320, 220));
|
||||||
|
|
||||||
|
// Since DrawGrid() renders based on viewport, let's also
|
||||||
|
// manually draw grid patterns to show different configurations
|
||||||
|
|
||||||
|
// Region 2: Custom fine grid pattern
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.2, 0.5, 0.2, 0.4));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
|
||||||
|
double gridX = 360;
|
||||||
|
double gridY = 20;
|
||||||
|
double gridW = 300;
|
||||||
|
double gridH = 200;
|
||||||
|
double step = 15;
|
||||||
|
|
||||||
|
// Vertical lines
|
||||||
|
for (double x = gridX; x <= gridX + gridW; x += step) {
|
||||||
|
gal->DrawLine(VECTOR2D(x, gridY), VECTOR2D(x, gridY + gridH));
|
||||||
|
}
|
||||||
|
// Horizontal lines
|
||||||
|
for (double y = gridY; y <= gridY + gridH; y += step) {
|
||||||
|
gal->DrawLine(VECTOR2D(gridX, y), VECTOR2D(gridX + gridW, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coarse grid overlay
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.7, 0.3, 0.6));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
double coarseStep = step * 5;
|
||||||
|
|
||||||
|
for (double x = gridX; x <= gridX + gridW; x += coarseStep) {
|
||||||
|
gal->DrawLine(VECTOR2D(x, gridY), VECTOR2D(x, gridY + gridH));
|
||||||
|
}
|
||||||
|
for (double y = gridY; y <= gridY + gridH; y += coarseStep) {
|
||||||
|
gal->DrawLine(VECTOR2D(gridX, y), VECTOR2D(gridX + gridW, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Region 2 boundary
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.8, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(gridX - 5, gridY - 5), VECTOR2D(gridX + gridW + 5, gridY + gridH + 5));
|
||||||
|
|
||||||
|
// Region 3: Dot grid (alternative grid style)
|
||||||
|
gal->SetLayerDepth(30);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.6, 0.6, 0.8, 0.6));
|
||||||
|
|
||||||
|
double dotGridX = 20;
|
||||||
|
double dotGridY = 260;
|
||||||
|
double dotStep = 25;
|
||||||
|
|
||||||
|
for (double x = dotGridX; x <= dotGridX + 280; x += dotStep) {
|
||||||
|
for (double y = dotGridY; y <= dotGridY + 200; y += dotStep) {
|
||||||
|
gal->DrawCircle(VECTOR2D(x, y), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark region 3
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.8, 0.8));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(dotGridX - 10, dotGridY - 10),
|
||||||
|
VECTOR2D(dotGridX + 290, dotGridY + 210));
|
||||||
|
|
||||||
|
// Region 4: Non-square grid (different X and Y spacing)
|
||||||
|
gal->SetLayerDepth(30);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.5, 0.3, 0.5));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
|
||||||
|
double nsGridX = 360;
|
||||||
|
double nsGridY = 260;
|
||||||
|
double nsGridW = 300;
|
||||||
|
double nsGridH = 200;
|
||||||
|
double xStep = 30;
|
||||||
|
double yStep = 15;
|
||||||
|
|
||||||
|
// Vertical lines (wide spacing)
|
||||||
|
for (double x = nsGridX; x <= nsGridX + nsGridW; x += xStep) {
|
||||||
|
gal->DrawLine(VECTOR2D(x, nsGridY), VECTOR2D(x, nsGridY + nsGridH));
|
||||||
|
}
|
||||||
|
// Horizontal lines (tight spacing)
|
||||||
|
for (double y = nsGridY; y <= nsGridY + nsGridH; y += yStep) {
|
||||||
|
gal->DrawLine(VECTOR2D(nsGridX, y), VECTOR2D(nsGridX + nsGridW, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark region 4
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.6, 0.4, 0.8));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(nsGridX - 5, nsGridY - 5),
|
||||||
|
VECTOR2D(nsGridX + nsGridW + 5, nsGridY + nsGridH + 5));
|
||||||
|
|
||||||
|
// Region 5: Grid with different origin (offset)
|
||||||
|
gal->SetLayerDepth(30);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.7, 0.3, 0.7, 0.5));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
|
||||||
|
double oGridX = 700;
|
||||||
|
double oGridY = 20;
|
||||||
|
double oGridW = 100;
|
||||||
|
double oGridH = 200;
|
||||||
|
double oStep = 20;
|
||||||
|
double originOffsetX = 7; // Origin offset from edge
|
||||||
|
double originOffsetY = 12;
|
||||||
|
|
||||||
|
for (double x = oGridX + originOffsetX; x <= oGridX + oGridW; x += oStep) {
|
||||||
|
gal->DrawLine(VECTOR2D(x, oGridY), VECTOR2D(x, oGridY + oGridH));
|
||||||
|
}
|
||||||
|
for (double y = oGridY + originOffsetY; y <= oGridY + oGridH; y += oStep) {
|
||||||
|
gal->DrawLine(VECTOR2D(oGridX, y), VECTOR2D(oGridX + oGridW, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark origin point
|
||||||
|
gal->SetFillColor(COLOR4D(1.0, 0.3, 0.3, 1.0));
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->DrawCircle(VECTOR2D(oGridX + originOffsetX, oGridY + originOffsetY), 5);
|
||||||
|
|
||||||
|
// Mark region 5
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.7, 0.4, 0.7, 0.8));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(oGridX - 5, oGridY - 5),
|
||||||
|
VECTOR2D(oGridX + oGridW + 5, oGridY + oGridH + 5));
|
||||||
|
|
||||||
|
// Axes demonstration in center-bottom region
|
||||||
|
gal->SetLayerDepth(20);
|
||||||
|
double axesCx = 700;
|
||||||
|
double axesCy = 380;
|
||||||
|
|
||||||
|
// X axis (red)
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.2, 0.2, 1.0));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawLine(VECTOR2D(axesCx - 80, axesCy), VECTOR2D(axesCx + 80, axesCy));
|
||||||
|
|
||||||
|
// Y axis (green)
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.2, 1.0, 0.2, 1.0));
|
||||||
|
gal->DrawLine(VECTOR2D(axesCx, axesCy - 80), VECTOR2D(axesCx, axesCy + 80));
|
||||||
|
|
||||||
|
// Origin marker
|
||||||
|
gal->SetFillColor(COLOR4D(1.0, 1.0, 0.2, 1.0));
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->DrawCircle(VECTOR2D(axesCx, axesCy), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
|
|
@ -186,6 +186,59 @@ void RenderGroupCaching(GAL* gal, int width, int height) {
|
||||||
for (int y = 0; y < height; y += 50) {
|
for (int y = 0; y < height; y += 50) {
|
||||||
gal->DrawLine(VECTOR2D(0, y), VECTOR2D(width, y));
|
gal->DrawLine(VECTOR2D(0, y), VECTOR2D(width, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test DeleteGroup - create a group, draw it, delete it, create another
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.2, 0.9, 1.0));
|
||||||
|
|
||||||
|
int tempGroup = gal->BeginGroup();
|
||||||
|
{
|
||||||
|
gal->DrawCircle(VECTOR2D(0, 0), 20);
|
||||||
|
}
|
||||||
|
gal->EndGroup();
|
||||||
|
|
||||||
|
// Draw the temporary group
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(600, 400));
|
||||||
|
gal->DrawGroup(tempGroup);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Delete the temporary group
|
||||||
|
gal->DeleteGroup(tempGroup);
|
||||||
|
|
||||||
|
// Create a new group after deletion (reuses ID potentially)
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.9, 0.9, 1.0));
|
||||||
|
int newGroup = gal->BeginGroup();
|
||||||
|
{
|
||||||
|
// Draw a diamond shape
|
||||||
|
std::deque<VECTOR2D> diamond = {
|
||||||
|
VECTOR2D(0, -20),
|
||||||
|
VECTOR2D(15, 0),
|
||||||
|
VECTOR2D(0, 20),
|
||||||
|
VECTOR2D(-15, 0)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(diamond);
|
||||||
|
}
|
||||||
|
gal->EndGroup();
|
||||||
|
|
||||||
|
// Draw the new group
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(680, 400));
|
||||||
|
gal->DrawGroup(newGroup);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Test ClearCache - clears all cached groups
|
||||||
|
// We'll draw markers showing the groups existed before clear
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 1.0, 1.0, 0.5));
|
||||||
|
gal->DrawRectangle(VECTOR2D(580, 380), VECTOR2D(720, 420));
|
||||||
|
|
||||||
|
// Note: ClearCache() invalidates all groups, so we call it at the end
|
||||||
|
// In production code, you'd recreate groups after ClearCache
|
||||||
|
gal->ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace GALTest
|
} // namespace GALTest
|
||||||
|
|
|
||||||
125
tests/gal-regression/scenarios/scenario_hole_walls.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
* Hole Walls Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL::DrawHoleWall() - ring-shaped holes
|
||||||
|
*
|
||||||
|
* This is used for drawing plated through holes (PTH) in PCBs
|
||||||
|
* where there's a drill hole surrounded by copper plating.
|
||||||
|
* Parameters: center point, inner radius (hole), wall width
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderHoleWalls(GAL* gal, int width, int height) {
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Row 1: Different hole sizes with same wall width
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.7, 0.3, 1.0)); // Copper color
|
||||||
|
double holeRadii[] = {5.0, 10.0, 15.0, 20.0, 25.0};
|
||||||
|
double wallWidth = 8.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
double x = 100 + i * 100;
|
||||||
|
gal->DrawHoleWall(VECTOR2D(x, 80), holeRadii[i], wallWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 2: Same hole size with different wall widths
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.2, 1.0));
|
||||||
|
double holeRadius = 12.0;
|
||||||
|
double wallWidths[] = {3.0, 6.0, 10.0, 15.0, 20.0};
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
double x = 100 + i * 100;
|
||||||
|
gal->DrawHoleWall(VECTOR2D(x, 180), holeRadius, wallWidths[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 3: Various colors (like different PCB layers)
|
||||||
|
double coloredRadius = 15.0;
|
||||||
|
double coloredWall = 10.0;
|
||||||
|
|
||||||
|
// Front copper
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.2, 0.2, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(100, 280), coloredRadius, coloredWall);
|
||||||
|
|
||||||
|
// Back copper
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.2, 0.9, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(200, 280), coloredRadius, coloredWall);
|
||||||
|
|
||||||
|
// Inner layer 1
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.8, 0.2, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(300, 280), coloredRadius, coloredWall);
|
||||||
|
|
||||||
|
// Inner layer 2
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.8, 0.2, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(400, 280), coloredRadius, coloredWall);
|
||||||
|
|
||||||
|
// Via (smaller)
|
||||||
|
gal->SetFillColor(COLOR4D(0.6, 0.6, 0.6, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(500, 280), 8.0, 6.0);
|
||||||
|
|
||||||
|
// Row 4: Semi-transparent overlapping (like layer stack view)
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.3, 0.3, 0.5));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(150, 380), 20.0, 15.0);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.9, 0.3, 0.5));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(180, 380), 18.0, 13.0);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.3, 0.9, 0.5));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(210, 380), 16.0, 11.0);
|
||||||
|
|
||||||
|
// Row 4 continued: Grid of small vias
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.7, 0.7, 1.0));
|
||||||
|
for (int row = 0; row < 3; row++) {
|
||||||
|
for (int col = 0; col < 5; col++) {
|
||||||
|
double x = 320 + col * 30;
|
||||||
|
double y = 350 + row * 30;
|
||||||
|
gal->DrawHoleWall(VECTOR2D(x, y), 5.0, 4.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 5: Very thin walls (micro vias)
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.5, 0.2, 1.0));
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
double x = 100 + i * 70;
|
||||||
|
gal->DrawHoleWall(VECTOR2D(x, 470), 8.0, 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Large mounting hole example
|
||||||
|
gal->SetFillColor(COLOR4D(0.6, 0.6, 0.3, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(650, 150), 30.0, 25.0);
|
||||||
|
|
||||||
|
// Very thick annular ring
|
||||||
|
gal->SetFillColor(COLOR4D(0.4, 0.7, 0.4, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(650, 300), 15.0, 35.0);
|
||||||
|
|
||||||
|
// Stroked hole wall (outline mode)
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 1.0, 0.0, 1.0));
|
||||||
|
gal->DrawHoleWall(VECTOR2D(650, 430), 20.0, 15.0);
|
||||||
|
|
||||||
|
// Labels area markers
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.5, 0.5));
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
|
||||||
|
// Row markers
|
||||||
|
gal->DrawLine(VECTOR2D(50, 40), VECTOR2D(550, 40));
|
||||||
|
gal->DrawLine(VECTOR2D(50, 140), VECTOR2D(550, 140));
|
||||||
|
gal->DrawLine(VECTOR2D(50, 240), VECTOR2D(550, 240));
|
||||||
|
gal->DrawLine(VECTOR2D(50, 320), VECTOR2D(550, 320));
|
||||||
|
gal->DrawLine(VECTOR2D(50, 440), VECTOR2D(650, 440));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
275
tests/gal-regression/scenarios/scenario_negative_mode.cpp
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
/**
|
||||||
|
* Negative Mode / Diff Layer Scenario
|
||||||
|
*
|
||||||
|
* Tests Gerber-style negative rendering concepts.
|
||||||
|
*
|
||||||
|
* IMPORTANT: In OPENGL_GAL these are mostly no-ops:
|
||||||
|
* - SetNegativeDrawMode() - NO-OP (Cairo uses CAIRO_OPERATOR_CLEAR)
|
||||||
|
* - StartNegativesLayer() / EndNegativesLayer() - NO-OP
|
||||||
|
* - StartDiffLayer() / EndDiffLayer() - Requires m_tempBuffer (compositor setup)
|
||||||
|
*
|
||||||
|
* This test:
|
||||||
|
* 1. Calls the APIs to verify they don't crash
|
||||||
|
* 2. Demonstrates what negative mode LOOKS like (simulated with layered drawing)
|
||||||
|
* 3. Shows PCB thermal relief patterns (common use case for negative mode)
|
||||||
|
*
|
||||||
|
* In Gerbview, negative objects "cut out" from the copper layer.
|
||||||
|
* The polarity is determined by: item->GetLayerPolarity() XOR image->m_ImageNegative
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderNegativeMode(GAL* gal, int width, int height) {
|
||||||
|
// Enable depth testing for proper layering
|
||||||
|
gal->EnableDepthTest(true);
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 1: Simulated thermal relief (what negative mode produces)
|
||||||
|
//=========================================================================
|
||||||
|
// This shows the RESULT of negative mode - holes cut in copper pour
|
||||||
|
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Dark background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(380, 280));
|
||||||
|
|
||||||
|
// Copper pour (ground plane)
|
||||||
|
gal->SetLayerDepth(80);
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.5, 0.2, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, 40), VECTOR2D(360, 260));
|
||||||
|
|
||||||
|
// Thermal relief pattern - simulated by drawing background color
|
||||||
|
// (In real Gerbview, SetNegativeDrawMode(true) + draw = erase)
|
||||||
|
gal->SetLayerDepth(60);
|
||||||
|
COLOR4D clearColor(0.12, 0.12, 0.15, 1.0); // "Cut through" to background
|
||||||
|
gal->SetFillColor(clearColor);
|
||||||
|
|
||||||
|
// First pad thermal - cross pattern
|
||||||
|
double pad1X = 120, pad1Y = 150;
|
||||||
|
double spokeLen = 35, spokeW = 5, clearance = 18;
|
||||||
|
|
||||||
|
// Clearance ring
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(8.0);
|
||||||
|
gal->SetStrokeColor(clearColor);
|
||||||
|
gal->DrawCircle(VECTOR2D(pad1X, pad1Y), clearance);
|
||||||
|
|
||||||
|
// Thermal spokes (horizontal and vertical)
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(clearColor);
|
||||||
|
gal->DrawRectangle(VECTOR2D(pad1X - clearance - spokeLen, pad1Y - spokeW/2),
|
||||||
|
VECTOR2D(pad1X - clearance, pad1Y + spokeW/2));
|
||||||
|
gal->DrawRectangle(VECTOR2D(pad1X + clearance, pad1Y - spokeW/2),
|
||||||
|
VECTOR2D(pad1X + clearance + spokeLen, pad1Y + spokeW/2));
|
||||||
|
gal->DrawRectangle(VECTOR2D(pad1X - spokeW/2, pad1Y - clearance - spokeLen),
|
||||||
|
VECTOR2D(pad1X + spokeW/2, pad1Y - clearance));
|
||||||
|
gal->DrawRectangle(VECTOR2D(pad1X - spokeW/2, pad1Y + clearance),
|
||||||
|
VECTOR2D(pad1X + spokeW/2, pad1Y + clearance + spokeLen));
|
||||||
|
|
||||||
|
// Pad on top
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
gal->SetFillColor(COLOR4D(0.85, 0.65, 0.25, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(pad1X, pad1Y), 14);
|
||||||
|
|
||||||
|
// Second pad thermal - diagonal spokes
|
||||||
|
double pad2X = 280, pad2Y = 150;
|
||||||
|
gal->SetLayerDepth(60);
|
||||||
|
gal->SetFillColor(clearColor);
|
||||||
|
|
||||||
|
// Diagonal thermal spokes
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
double angle = i * M_PI / 2 + M_PI / 4;
|
||||||
|
double x1 = pad2X + cos(angle) * clearance;
|
||||||
|
double y1 = pad2Y + sin(angle) * clearance;
|
||||||
|
double x2 = pad2X + cos(angle) * (clearance + spokeLen);
|
||||||
|
double y2 = pad2Y + sin(angle) * (clearance + spokeLen);
|
||||||
|
gal->DrawSegment(VECTOR2D(x1, y1), VECTOR2D(x2, y2), spokeW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clearance ring
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(8.0);
|
||||||
|
gal->SetStrokeColor(clearColor);
|
||||||
|
gal->DrawCircle(VECTOR2D(pad2X, pad2Y), clearance);
|
||||||
|
|
||||||
|
// Pad
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.85, 0.65, 0.25, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(pad2X, pad2Y), 14);
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 2: API calls test (verify they don't crash)
|
||||||
|
//=========================================================================
|
||||||
|
// These are NO-OPs in OpenGL but we call them to test the API
|
||||||
|
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 20), VECTOR2D(780, 280));
|
||||||
|
|
||||||
|
// Draw base content
|
||||||
|
gal->SetLayerDepth(80);
|
||||||
|
gal->SetFillColor(COLOR4D(0.6, 0.3, 0.6, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(420, 40), VECTOR2D(760, 260));
|
||||||
|
|
||||||
|
// Test SetNegativeDrawMode API (NO-OP in OpenGL)
|
||||||
|
gal->SetNegativeDrawMode(true);
|
||||||
|
|
||||||
|
// In Cairo, this would ERASE. In OpenGL, it draws normally.
|
||||||
|
gal->SetLayerDepth(60);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(500, 150), 35);
|
||||||
|
gal->DrawCircle(VECTOR2D(590, 150), 35);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 150), 35);
|
||||||
|
|
||||||
|
// Disable negative mode
|
||||||
|
gal->SetNegativeDrawMode(false);
|
||||||
|
|
||||||
|
// Draw something after to show mode was reset
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.9, 0.3, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(500, 150), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(590, 150), 15);
|
||||||
|
gal->DrawCircle(VECTOR2D(680, 150), 15);
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 3: Negatives layer API test
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.15, 0.12, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 300), VECTOR2D(380, 580));
|
||||||
|
|
||||||
|
// Base content before negatives layer
|
||||||
|
gal->SetLayerDepth(80);
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.6, 0.3, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, 320), VECTOR2D(360, 560));
|
||||||
|
|
||||||
|
// Test StartNegativesLayer / EndNegativesLayer (NO-OP in OpenGL)
|
||||||
|
gal->StartNegativesLayer();
|
||||||
|
|
||||||
|
// Content that would be on negatives layer
|
||||||
|
gal->SetLayerDepth(60);
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.15, 0.12, 1.0));
|
||||||
|
|
||||||
|
// Via clearances (simulated)
|
||||||
|
for (int row = 0; row < 2; row++) {
|
||||||
|
for (int col = 0; col < 4; col++) {
|
||||||
|
double x = 80 + col * 80;
|
||||||
|
double y = 380 + row * 100;
|
||||||
|
gal->DrawCircle(VECTOR2D(x, y), 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gal->EndNegativesLayer();
|
||||||
|
|
||||||
|
// Vias on top
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
gal->SetFillColor(COLOR4D(0.85, 0.65, 0.25, 1.0));
|
||||||
|
for (int row = 0; row < 2; row++) {
|
||||||
|
for (int col = 0; col < 4; col++) {
|
||||||
|
double x = 80 + col * 80;
|
||||||
|
double y = 380 + row * 100;
|
||||||
|
gal->DrawCircle(VECTOR2D(x, y), 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drill holes
|
||||||
|
gal->SetLayerDepth(20);
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.1, 0.1, 1.0));
|
||||||
|
for (int row = 0; row < 2; row++) {
|
||||||
|
for (int col = 0; col < 4; col++) {
|
||||||
|
double x = 80 + col * 80;
|
||||||
|
double y = 380 + row * 100;
|
||||||
|
gal->DrawCircle(VECTOR2D(x, y), 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 4: What "show negative objects" mode looks like
|
||||||
|
//=========================================================================
|
||||||
|
// In Gerbview, there's an option to show negative objects in a highlight color
|
||||||
|
// instead of actually cutting them out
|
||||||
|
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.12, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 300), VECTOR2D(780, 580));
|
||||||
|
|
||||||
|
// Copper layer
|
||||||
|
gal->SetLayerDepth(80);
|
||||||
|
gal->SetFillColor(COLOR4D(0.6, 0.5, 0.2, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(420, 320), VECTOR2D(760, 560));
|
||||||
|
|
||||||
|
// "Negative objects" shown as semi-transparent overlay (like Gerbview's show_negative_objects mode)
|
||||||
|
gal->SetLayerDepth(60);
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.8, 0.8, 0.5)); // Cyan highlight for negatives
|
||||||
|
|
||||||
|
// Trace clearance
|
||||||
|
gal->DrawSegment(VECTOR2D(440, 380), VECTOR2D(740, 380), 25);
|
||||||
|
|
||||||
|
// Pad clearances
|
||||||
|
gal->DrawCircle(VECTOR2D(480, 380), 20);
|
||||||
|
gal->DrawCircle(VECTOR2D(590, 380), 20);
|
||||||
|
gal->DrawCircle(VECTOR2D(700, 380), 20);
|
||||||
|
|
||||||
|
// Via clearances
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
gal->DrawCircle(VECTOR2D(480 + i * 110, 480), 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual traces and pads (drawn on top)
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.2, 1.0));
|
||||||
|
gal->DrawSegment(VECTOR2D(440, 380), VECTOR2D(740, 380), 10);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.7, 0.3, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(480, 380), 14);
|
||||||
|
gal->DrawCircle(VECTOR2D(590, 380), 14);
|
||||||
|
gal->DrawCircle(VECTOR2D(700, 380), 14);
|
||||||
|
gal->DrawCircle(VECTOR2D(480, 480), 12);
|
||||||
|
gal->DrawCircle(VECTOR2D(590, 480), 12);
|
||||||
|
gal->DrawCircle(VECTOR2D(700, 480), 12);
|
||||||
|
|
||||||
|
// Holes
|
||||||
|
gal->SetLayerDepth(20);
|
||||||
|
gal->SetFillColor(COLOR4D(0.1, 0.1, 0.1, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(480, 380), 5);
|
||||||
|
gal->DrawCircle(VECTOR2D(590, 380), 5);
|
||||||
|
gal->DrawCircle(VECTOR2D(700, 380), 5);
|
||||||
|
gal->DrawCircle(VECTOR2D(480, 480), 4);
|
||||||
|
gal->DrawCircle(VECTOR2D(590, 480), 4);
|
||||||
|
gal->DrawCircle(VECTOR2D(700, 480), 4);
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section frames
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(5);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(380, 280));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 20), VECTOR2D(780, 280));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 300), VECTOR2D(380, 580));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 300), VECTOR2D(780, 580));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
155
tests/gal-regression/scenarios/scenario_polylines_multi.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
/**
|
||||||
|
* Polylines Multi Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL::DrawPolylines() - drawing multiple polylines in a single call
|
||||||
|
*
|
||||||
|
* This is more efficient than calling DrawPolyline() multiple times
|
||||||
|
* when rendering many polylines with the same style.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderPolylinesMulti(GAL* gal, int width, int height) {
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
|
||||||
|
// Test 1: Multiple horizontal lines at once
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.3, 0.3, 1.0));
|
||||||
|
std::vector<std::vector<VECTOR2D>> horizontalLines;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
std::vector<VECTOR2D> line = {
|
||||||
|
VECTOR2D(50, 50 + i * 25),
|
||||||
|
VECTOR2D(250, 50 + i * 25)
|
||||||
|
};
|
||||||
|
horizontalLines.push_back(line);
|
||||||
|
}
|
||||||
|
gal->DrawPolylines(horizontalLines);
|
||||||
|
|
||||||
|
// Test 2: Multiple zigzag patterns
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 1.0, 0.3, 1.0));
|
||||||
|
std::vector<std::vector<VECTOR2D>> zigzags;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
std::vector<VECTOR2D> zig;
|
||||||
|
double baseY = 200 + i * 60;
|
||||||
|
for (int j = 0; j <= 6; j++) {
|
||||||
|
double x = 50 + j * 40;
|
||||||
|
double y = baseY + ((j % 2 == 0) ? 0 : 30);
|
||||||
|
zig.push_back(VECTOR2D(x, y));
|
||||||
|
}
|
||||||
|
zigzags.push_back(zig);
|
||||||
|
}
|
||||||
|
gal->DrawPolylines(zigzags);
|
||||||
|
|
||||||
|
// Test 3: Multiple wave patterns
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.3, 1.0, 1.0));
|
||||||
|
std::vector<std::vector<VECTOR2D>> waves;
|
||||||
|
for (int w = 0; w < 4; w++) {
|
||||||
|
std::vector<VECTOR2D> wave;
|
||||||
|
double baseY = 420 + w * 30;
|
||||||
|
for (int i = 0; i <= 20; i++) {
|
||||||
|
double t = (double)i / 20.0;
|
||||||
|
double x = 50 + t * 300;
|
||||||
|
double y = baseY + sin(t * M_PI * 3 + w * 0.5) * 10;
|
||||||
|
wave.push_back(VECTOR2D(x, y));
|
||||||
|
}
|
||||||
|
waves.push_back(wave);
|
||||||
|
}
|
||||||
|
gal->DrawPolylines(waves);
|
||||||
|
|
||||||
|
// Test 4: Grid pattern using polylines
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.8, 0.2, 1.0));
|
||||||
|
std::vector<std::vector<VECTOR2D>> gridLines;
|
||||||
|
|
||||||
|
// Vertical grid lines
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
double x = 400 + i * 40;
|
||||||
|
gridLines.push_back({VECTOR2D(x, 50), VECTOR2D(x, 250)});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal grid lines
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
double y = 50 + i * 40;
|
||||||
|
gridLines.push_back({VECTOR2D(400, y), VECTOR2D(680, y)});
|
||||||
|
}
|
||||||
|
|
||||||
|
gal->DrawPolylines(gridLines);
|
||||||
|
|
||||||
|
// Test 5: Multiple concentric shapes
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.3, 0.8, 1.0));
|
||||||
|
std::vector<std::vector<VECTOR2D>> concentricSquares;
|
||||||
|
double cx = 550;
|
||||||
|
double cy = 400;
|
||||||
|
|
||||||
|
for (int i = 1; i <= 4; i++) {
|
||||||
|
double size = i * 25;
|
||||||
|
std::vector<VECTOR2D> square = {
|
||||||
|
VECTOR2D(cx - size, cy - size),
|
||||||
|
VECTOR2D(cx + size, cy - size),
|
||||||
|
VECTOR2D(cx + size, cy + size),
|
||||||
|
VECTOR2D(cx - size, cy + size),
|
||||||
|
VECTOR2D(cx - size, cy - size) // close
|
||||||
|
};
|
||||||
|
concentricSquares.push_back(square);
|
||||||
|
}
|
||||||
|
gal->DrawPolylines(concentricSquares);
|
||||||
|
|
||||||
|
// Test 6: Star burst pattern
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.5, 0.0, 1.0));
|
||||||
|
std::vector<std::vector<VECTOR2D>> starBurst;
|
||||||
|
double starCx = 720;
|
||||||
|
double starCy = 150;
|
||||||
|
double innerR = 20;
|
||||||
|
double outerR = 60;
|
||||||
|
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
double angle = i * M_PI / 6;
|
||||||
|
std::vector<VECTOR2D> ray = {
|
||||||
|
VECTOR2D(starCx + cos(angle) * innerR, starCy + sin(angle) * innerR),
|
||||||
|
VECTOR2D(starCx + cos(angle) * outerR, starCy + sin(angle) * outerR)
|
||||||
|
};
|
||||||
|
starBurst.push_back(ray);
|
||||||
|
}
|
||||||
|
gal->DrawPolylines(starBurst);
|
||||||
|
|
||||||
|
// Test 7: Parallel diagonal lines
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.8, 0.8, 1.0));
|
||||||
|
gal->SetLineWidth(1.5);
|
||||||
|
std::vector<std::vector<VECTOR2D>> diagonals;
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
double offset = i * 15;
|
||||||
|
diagonals.push_back({
|
||||||
|
VECTOR2D(400 + offset, 280),
|
||||||
|
VECTOR2D(520 + offset, 370)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
gal->DrawPolylines(diagonals);
|
||||||
|
|
||||||
|
// Test 8: Different line widths (multiple calls needed)
|
||||||
|
double lineWidths[] = {1.0, 2.0, 3.0, 5.0};
|
||||||
|
for (int w = 0; w < 4; w++) {
|
||||||
|
double t = (double)w / 3.0;
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0 - t * 0.5, 0.3 + t * 0.4, 0.2 + t * 0.6, 1.0));
|
||||||
|
gal->SetLineWidth(lineWidths[w]);
|
||||||
|
|
||||||
|
std::vector<std::vector<VECTOR2D>> widthDemo;
|
||||||
|
double y = 280 + w * 25;
|
||||||
|
widthDemo.push_back({VECTOR2D(50, y), VECTOR2D(180, y)});
|
||||||
|
widthDemo.push_back({VECTOR2D(200, y), VECTOR2D(330, y)});
|
||||||
|
gal->DrawPolylines(widthDemo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
261
tests/gal-regression/scenarios/scenario_render_targets.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
/**
|
||||||
|
* Render Targets Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL render target concepts:
|
||||||
|
* - SetTarget() / GetTarget()
|
||||||
|
* - ClearTarget()
|
||||||
|
* - HasTarget()
|
||||||
|
*
|
||||||
|
* NOTE: In this test harness context, we don't switch targets mid-frame
|
||||||
|
* as that requires specific compositor setup. Instead, we demonstrate
|
||||||
|
* the concept visually and test the API availability.
|
||||||
|
*
|
||||||
|
* RENDER_TARGET values:
|
||||||
|
* - TARGET_CACHED: Main rendering target (persistent)
|
||||||
|
* - TARGET_NONCACHED: Auxiliary target (cleared each frame)
|
||||||
|
* - TARGET_OVERLAY: Overlay items (cleared each frame)
|
||||||
|
* - TARGET_TEMP: Temporary target for special operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <gal/definitions.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
using KIGFX::RENDER_TARGET;
|
||||||
|
using KIGFX::TARGET_CACHED;
|
||||||
|
using KIGFX::TARGET_NONCACHED;
|
||||||
|
using KIGFX::TARGET_OVERLAY;
|
||||||
|
using KIGFX::TARGET_TEMP;
|
||||||
|
|
||||||
|
void RenderRenderTargets(GAL* gal, int width, int height) {
|
||||||
|
// All drawing to the default target (NONCACHED in test harness)
|
||||||
|
|
||||||
|
// Background grid
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.2, 0.2, 0.25, 0.4));
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x += 30) {
|
||||||
|
gal->DrawLine(VECTOR2D(x, 0), VECTOR2D(x, height));
|
||||||
|
}
|
||||||
|
for (int y = 0; y < height; y += 30) {
|
||||||
|
gal->DrawLine(VECTOR2D(0, y), VECTOR2D(width, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visual representation of render target concept
|
||||||
|
// Show boxes representing each target type
|
||||||
|
|
||||||
|
double boxW = 160;
|
||||||
|
double boxH = 200;
|
||||||
|
double startX = 40;
|
||||||
|
double startY = 50;
|
||||||
|
double spacing = 180;
|
||||||
|
|
||||||
|
// Box 1: CACHED (persistent content)
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.3, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(startX, startY), VECTOR2D(startX + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Static PCB elements (what would go in cached)
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.5, 0.2, 1.0));
|
||||||
|
gal->DrawSegment(VECTOR2D(startX + 20, startY + 50), VECTOR2D(startX + boxW - 20, startY + 50), 6);
|
||||||
|
gal->DrawSegment(VECTOR2D(startX + 20, startY + 100), VECTOR2D(startX + boxW - 20, startY + 100), 6);
|
||||||
|
gal->DrawSegment(VECTOR2D(startX + 20, startY + 150), VECTOR2D(startX + boxW - 20, startY + 150), 6);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.3, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + 40, startY + 50), 12);
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + boxW - 40, startY + 50), 12);
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + 40, startY + 100), 12);
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + boxW - 40, startY + 100), 12);
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + 40, startY + 150), 12);
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + boxW - 40, startY + 150), 12);
|
||||||
|
|
||||||
|
// Frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.6, 0.3, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(startX, startY), VECTOR2D(startX + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.8, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(startX + 20, startY + boxH + 10), VECTOR2D(startX + boxW - 20, startY + boxH + 25));
|
||||||
|
|
||||||
|
// Box 2: NONCACHED (dynamic content)
|
||||||
|
double box2X = startX + spacing;
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.25, 0.1, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box2X, startY), VECTOR2D(box2X + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Dynamic elements (selection highlights, moving items)
|
||||||
|
gal->SetFillColor(COLOR4D(1.0, 1.0, 0.2, 0.4));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box2X + 30, startY + 40), VECTOR2D(box2X + boxW - 30, startY + 80));
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.8, 0.2, 0.6));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box2X + 50, startY + 90), VECTOR2D(box2X + boxW - 50, startY + 140));
|
||||||
|
|
||||||
|
// Movement arrows
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.5, 0.0, 0.8));
|
||||||
|
gal->DrawLine(VECTOR2D(box2X + 80, startY + 160), VECTOR2D(box2X + 80, startY + 180));
|
||||||
|
gal->DrawLine(VECTOR2D(box2X + 80, startY + 180), VECTOR2D(box2X + 70, startY + 170));
|
||||||
|
gal->DrawLine(VECTOR2D(box2X + 80, startY + 180), VECTOR2D(box2X + 90, startY + 170));
|
||||||
|
|
||||||
|
// Frame
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.7, 0.2, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box2X, startY), VECTOR2D(box2X + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.9, 0.8, 0.3, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box2X + 20, startY + boxH + 10), VECTOR2D(box2X + boxW - 20, startY + boxH + 25));
|
||||||
|
|
||||||
|
// Box 3: OVERLAY (crosshairs, measurements)
|
||||||
|
double box3X = startX + spacing * 2;
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.25, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box3X, startY), VECTOR2D(box3X + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Crosshair overlay
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 1.0, 1.0, 0.8));
|
||||||
|
double crossX = box3X + boxW / 2;
|
||||||
|
double crossY = startY + boxH / 2;
|
||||||
|
gal->DrawLine(VECTOR2D(box3X + 10, crossY), VECTOR2D(box3X + boxW - 10, crossY));
|
||||||
|
gal->DrawLine(VECTOR2D(crossX, startY + 10), VECTOR2D(crossX, startY + boxH - 10));
|
||||||
|
|
||||||
|
// Measurement line
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 1.0, 1.0, 0.9));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawLine(VECTOR2D(box3X + 30, startY + 160), VECTOR2D(box3X + boxW - 30, startY + 160));
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 1.0, 1.0, 0.9));
|
||||||
|
gal->DrawCircle(VECTOR2D(box3X + 30, startY + 160), 4);
|
||||||
|
gal->DrawCircle(VECTOR2D(box3X + boxW - 30, startY + 160), 4);
|
||||||
|
|
||||||
|
// Frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.8, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box3X, startY), VECTOR2D(box3X + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.9, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box3X + 20, startY + boxH + 10), VECTOR2D(box3X + boxW - 20, startY + boxH + 25));
|
||||||
|
|
||||||
|
// Box 4: TEMP (special operations)
|
||||||
|
double box4X = startX + spacing * 3;
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.25, 0.15, 0.25, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box4X, startY), VECTOR2D(box4X + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Temporary rendering (drag preview, etc)
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.3, 0.8, 0.5));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box4X + 40, startY + 60), VECTOR2D(box4X + boxW - 40, startY + 120));
|
||||||
|
|
||||||
|
// Dotted outline showing "ghost" position
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.5, 1.0, 0.6));
|
||||||
|
// Draw dashed rectangle manually
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
double x1 = box4X + 40 + i * 8;
|
||||||
|
double x2 = x1 + 5;
|
||||||
|
if (x2 > box4X + boxW - 40) x2 = box4X + boxW - 40;
|
||||||
|
gal->DrawLine(VECTOR2D(x1, startY + 140), VECTOR2D(x2, startY + 140));
|
||||||
|
gal->DrawLine(VECTOR2D(x1, startY + 180), VECTOR2D(x2, startY + 180));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.7, 0.3, 0.7, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box4X, startY), VECTOR2D(box4X + boxW, startY + boxH));
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.8, 0.4, 0.8, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(box4X + 20, startY + boxH + 10), VECTOR2D(box4X + boxW - 20, startY + boxH + 25));
|
||||||
|
|
||||||
|
// Bottom: API demonstration - HasTarget results
|
||||||
|
double apiY = 340;
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
|
||||||
|
// Check target availability (these are API calls)
|
||||||
|
bool hasCached = gal->HasTarget(TARGET_CACHED);
|
||||||
|
bool hasNoncached = gal->HasTarget(TARGET_NONCACHED);
|
||||||
|
bool hasOverlay = gal->HasTarget(TARGET_OVERLAY);
|
||||||
|
bool hasTemp = gal->HasTarget(TARGET_TEMP);
|
||||||
|
|
||||||
|
// Show results visually
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Label area
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.18, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, apiY), VECTOR2D(760, apiY + 100));
|
||||||
|
|
||||||
|
// Indicator for each target
|
||||||
|
double indicatorY = apiY + 50;
|
||||||
|
double indicatorSpacing = 180;
|
||||||
|
|
||||||
|
// CACHED indicator
|
||||||
|
gal->SetFillColor(hasCached ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + boxW / 2, indicatorY), 15);
|
||||||
|
|
||||||
|
// NONCACHED indicator
|
||||||
|
gal->SetFillColor(hasNoncached ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + spacing + boxW / 2, indicatorY), 15);
|
||||||
|
|
||||||
|
// OVERLAY indicator
|
||||||
|
gal->SetFillColor(hasOverlay ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + spacing * 2 + boxW / 2, indicatorY), 15);
|
||||||
|
|
||||||
|
// TEMP indicator
|
||||||
|
gal->SetFillColor(hasTemp ? COLOR4D(0.2, 0.9, 0.2, 1.0) : COLOR4D(0.9, 0.2, 0.2, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(startX + spacing * 3 + boxW / 2, indicatorY), 15);
|
||||||
|
|
||||||
|
// Frame around API section
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.4, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, apiY), VECTOR2D(760, apiY + 100));
|
||||||
|
|
||||||
|
// Bottom: GetTarget demonstration
|
||||||
|
RENDER_TARGET currentTarget = gal->GetTarget();
|
||||||
|
(void)currentTarget; // Used - shows API works
|
||||||
|
|
||||||
|
// Label for current target
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, apiY + 70), VECTOR2D(300, apiY + 85));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
271
tests/gal-regression/scenarios/scenario_screen_transform.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
/**
|
||||||
|
* Screen Transform Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL screen-level transformation methods:
|
||||||
|
* - SetRotation() / GetRotation() - screen rotation
|
||||||
|
* - SetFlip() - X/Y axis flipping
|
||||||
|
* - ToWorld() / ToScreen() - coordinate conversion
|
||||||
|
*
|
||||||
|
* These are viewport-level transforms that affect all rendering,
|
||||||
|
* different from the per-object Save/Restore/Transform methods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderScreenTransform(GAL* gal, int width, int height) {
|
||||||
|
// Note: SetRotation and SetFlip affect the world-to-screen matrix
|
||||||
|
// They need to be set before drawing and affect subsequent operations
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Test SetFlip() and SetRotation() APIs
|
||||||
|
// These are screen-level transforms that affect the worldScreenMatrix
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
// Test SetFlip() API - sets X and/or Y axis mirroring
|
||||||
|
// Note: In our test harness the matrix is already computed, so we demonstrate
|
||||||
|
// the API is callable. In real use, SetFlip must be called before ComputeWorldScreenMatrix
|
||||||
|
gal->SetFlip(false, false); // No flip - default state
|
||||||
|
|
||||||
|
// Test SetRotation() API - sets screen rotation angle
|
||||||
|
// Note: Like SetFlip, affects worldScreenMatrix computation
|
||||||
|
gal->SetRotation(0.0); // No rotation - default state
|
||||||
|
|
||||||
|
// First, draw reference content without any screen transforms
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(0, 0), VECTOR2D(width, height));
|
||||||
|
|
||||||
|
// Reference grid
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.2, 0.2, 0.25, 0.4));
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x += 40) {
|
||||||
|
gal->DrawLine(VECTOR2D(x, 0), VECTOR2D(x, height));
|
||||||
|
}
|
||||||
|
for (int y = 0; y < height; y += 40) {
|
||||||
|
gal->DrawLine(VECTOR2D(0, y), VECTOR2D(width, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Normal orientation reference shape
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Draw an arrow-like shape that shows orientation
|
||||||
|
auto drawOrientationMarker = [&](double cx, double cy, double size, COLOR4D color) {
|
||||||
|
gal->SetFillColor(color);
|
||||||
|
|
||||||
|
// Main body (rectangle)
|
||||||
|
gal->DrawRectangle(VECTOR2D(cx - size * 0.3, cy - size * 0.5),
|
||||||
|
VECTOR2D(cx + size * 0.3, cy + size * 0.3));
|
||||||
|
|
||||||
|
// Arrow head pointing up
|
||||||
|
std::deque<VECTOR2D> arrow = {
|
||||||
|
VECTOR2D(cx, cy - size * 0.8),
|
||||||
|
VECTOR2D(cx - size * 0.5, cy - size * 0.3),
|
||||||
|
VECTOR2D(cx + size * 0.5, cy - size * 0.3)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(arrow);
|
||||||
|
|
||||||
|
// Small circle at base to show which end is bottom
|
||||||
|
gal->SetFillColor(COLOR4D(color.r * 0.5, color.g * 0.5, color.b * 0.5, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(cx, cy + size * 0.15), size * 0.15);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reference marker (no transform)
|
||||||
|
drawOrientationMarker(120, 120, 60, COLOR4D(0.8, 0.3, 0.3, 1.0));
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.3, 0.3, 0.8));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(60, 50), VECTOR2D(180, 180));
|
||||||
|
|
||||||
|
// Test 2: Using Save/Restore with rotation (object-level transform)
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(280, 120));
|
||||||
|
gal->Rotate(45.0 * M_PI / 180.0);
|
||||||
|
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.8, 0.3, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-30, -50), VECTOR2D(30, 30));
|
||||||
|
std::deque<VECTOR2D> arrow2 = {
|
||||||
|
VECTOR2D(0, -80),
|
||||||
|
VECTOR2D(-50, -30),
|
||||||
|
VECTOR2D(50, -30)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(arrow2);
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.4, 0.15, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(0, 15), 15);
|
||||||
|
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.6, 0.3, 0.8));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(200, 50), VECTOR2D(360, 200));
|
||||||
|
|
||||||
|
// Test 3: Demonstrate ToWorld/ToScreen coordinate conversion
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
|
||||||
|
// Draw a marker at a known world position
|
||||||
|
VECTOR2D worldPoint(500, 120);
|
||||||
|
|
||||||
|
// Mark the world position
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.8, 0.2, 1.0));
|
||||||
|
gal->DrawCircle(worldPoint, 15);
|
||||||
|
|
||||||
|
// Convert to screen and back
|
||||||
|
VECTOR2D screenPoint = gal->ToScreen(worldPoint);
|
||||||
|
VECTOR2D backToWorld = gal->ToWorld(screenPoint);
|
||||||
|
|
||||||
|
// Draw indicator showing the conversion (should be at same spot)
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.2, 0.8, 0.8, 1.0));
|
||||||
|
gal->DrawCircle(backToWorld, 20);
|
||||||
|
|
||||||
|
// Label
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.2, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(440, 50), VECTOR2D(560, 180));
|
||||||
|
|
||||||
|
// Test 4: Multiple rotated shapes showing different angles
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
double angles[] = {0, 30, 60, 90, 120, 150};
|
||||||
|
double baseX = 100;
|
||||||
|
double baseY = 300;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
double cx = baseX + i * 100;
|
||||||
|
double angle = angles[i] * M_PI / 180.0;
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(cx, baseY));
|
||||||
|
gal->Rotate(angle);
|
||||||
|
|
||||||
|
// Draw a simple "F" shape to show rotation clearly
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
double t = (double)i / 5.0;
|
||||||
|
gal->SetFillColor(COLOR4D(0.8 - t * 0.3, 0.3 + t * 0.5, 0.3 + t * 0.3, 1.0));
|
||||||
|
|
||||||
|
// Vertical bar
|
||||||
|
gal->DrawRectangle(VECTOR2D(-5, -30), VECTOR2D(5, 30));
|
||||||
|
// Top horizontal bar
|
||||||
|
gal->DrawRectangle(VECTOR2D(5, -30), VECTOR2D(25, -20));
|
||||||
|
// Middle horizontal bar
|
||||||
|
gal->DrawRectangle(VECTOR2D(5, -5), VECTOR2D(18, 5));
|
||||||
|
|
||||||
|
gal->Restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame around rotation demo
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.6, 0.8));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, 230), VECTOR2D(660, 370));
|
||||||
|
|
||||||
|
// Test 5: Flip demonstration using object transforms
|
||||||
|
// (Note: SetFlip() affects the entire viewport, so we simulate with Scale)
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
// Original
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(120, 450));
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.4, 0.7, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-25, -40), VECTOR2D(25, 20));
|
||||||
|
std::deque<VECTOR2D> tri1 = {
|
||||||
|
VECTOR2D(0, -60), VECTOR2D(-30, -40), VECTOR2D(30, -40)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(tri1);
|
||||||
|
gal->SetFillColor(COLOR4D(0.35, 0.2, 0.35, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(0, 5), 10);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// X-flipped (mirror horizontally)
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(280, 450));
|
||||||
|
gal->Scale(VECTOR2D(-1.0, 1.0)); // Flip X
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.4, 0.7, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-25, -40), VECTOR2D(25, 20));
|
||||||
|
std::deque<VECTOR2D> tri2 = {
|
||||||
|
VECTOR2D(0, -60), VECTOR2D(-30, -40), VECTOR2D(30, -40)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(tri2);
|
||||||
|
gal->SetFillColor(COLOR4D(0.35, 0.2, 0.35, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(0, 5), 10);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Y-flipped (mirror vertically)
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(440, 450));
|
||||||
|
gal->Scale(VECTOR2D(1.0, -1.0)); // Flip Y
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.4, 0.7, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-25, -40), VECTOR2D(25, 20));
|
||||||
|
std::deque<VECTOR2D> tri3 = {
|
||||||
|
VECTOR2D(0, -60), VECTOR2D(-30, -40), VECTOR2D(30, -40)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(tri3);
|
||||||
|
gal->SetFillColor(COLOR4D(0.35, 0.2, 0.35, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(0, 5), 10);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Both flipped
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(600, 450));
|
||||||
|
gal->Scale(VECTOR2D(-1.0, -1.0)); // Flip both
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.4, 0.7, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-25, -40), VECTOR2D(25, 20));
|
||||||
|
std::deque<VECTOR2D> tri4 = {
|
||||||
|
VECTOR2D(0, -60), VECTOR2D(-30, -40), VECTOR2D(30, -40)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(tri4);
|
||||||
|
gal->SetFillColor(COLOR4D(0.35, 0.2, 0.35, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(0, 5), 10);
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Labels for flip demo
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(1.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.3, 0.5, 0.6));
|
||||||
|
gal->DrawRectangle(VECTOR2D(70, 385), VECTOR2D(170, 395)); // Original
|
||||||
|
gal->DrawRectangle(VECTOR2D(230, 385), VECTOR2D(330, 395)); // X-flip
|
||||||
|
gal->DrawRectangle(VECTOR2D(390, 385), VECTOR2D(490, 395)); // Y-flip
|
||||||
|
gal->DrawRectangle(VECTOR2D(550, 385), VECTOR2D(650, 395)); // XY-flip
|
||||||
|
|
||||||
|
// Frame around flip demo
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.4, 0.6, 0.8));
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, 380), VECTOR2D(700, 510));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
377
tests/gal-regression/scenarios/scenario_text_attrs.cpp
Normal file
|
|
@ -0,0 +1,377 @@
|
||||||
|
/**
|
||||||
|
* Text Attributes Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL text attribute methods:
|
||||||
|
* - SetGlyphSize() / GetGlyphSize()
|
||||||
|
* - SetFontBold() / IsFontBold()
|
||||||
|
* - SetFontItalic() / IsFontItalic()
|
||||||
|
* - SetFontUnderlined() / IsFontUnderlined()
|
||||||
|
* - SetTextMirrored() / IsTextMirrored()
|
||||||
|
* - SetHorizontalJustify() / GetHorizontalJustify()
|
||||||
|
* - SetVerticalJustify() / GetVerticalJustify()
|
||||||
|
* - ResetTextAttributes()
|
||||||
|
*
|
||||||
|
* Note: These methods set internal m_attributes member variables.
|
||||||
|
* Actual text rendering requires KIFONT infrastructure which we don't stub.
|
||||||
|
* This scenario tests the APIs are callable and demonstrates their purpose
|
||||||
|
* by drawing visual indicators of what the attributes would affect.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderTextAttrs(GAL* gal, int width, int height) {
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(0, 0), VECTOR2D(width, height));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 1: SetGlyphSize() / GetGlyphSize()
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
// Test SetGlyphSize with different sizes
|
||||||
|
VECTOR2D smallSize(10, 12);
|
||||||
|
VECTOR2D mediumSize(20, 24);
|
||||||
|
VECTOR2D largeSize(40, 48);
|
||||||
|
|
||||||
|
gal->SetGlyphSize(smallSize);
|
||||||
|
VECTOR2D currentSize = gal->GetGlyphSize();
|
||||||
|
// currentSize should now be (10, 12)
|
||||||
|
|
||||||
|
gal->SetGlyphSize(mediumSize);
|
||||||
|
gal->SetGlyphSize(largeSize);
|
||||||
|
|
||||||
|
// Visual: Draw rectangles showing glyph sizes
|
||||||
|
double baseX = 50, baseY = 60;
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.6, 0.8, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(baseX, baseY),
|
||||||
|
VECTOR2D(baseX + smallSize.x, baseY + smallSize.y));
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.4, 0.7, 0.8, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(baseX + 30, baseY),
|
||||||
|
VECTOR2D(baseX + 30 + mediumSize.x, baseY + mediumSize.y));
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.5, 0.8, 0.8, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(baseX + 80, baseY),
|
||||||
|
VECTOR2D(baseX + 80 + largeSize.x, baseY + largeSize.y));
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.6, 0.7, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(200, 130));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 2: SetFontBold() / SetFontItalic() / SetFontUnderlined()
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Test font style APIs
|
||||||
|
gal->SetFontBold(false);
|
||||||
|
bool isBold = gal->IsFontBold(); // Should be false
|
||||||
|
|
||||||
|
gal->SetFontItalic(false);
|
||||||
|
bool isItalic = gal->IsFontItalic(); // Should be false
|
||||||
|
|
||||||
|
gal->SetFontUnderlined(false);
|
||||||
|
bool isUnderlined = gal->IsFontUnderlined(); // Should be false
|
||||||
|
|
||||||
|
// Set all to true
|
||||||
|
gal->SetFontBold(true);
|
||||||
|
gal->SetFontItalic(true);
|
||||||
|
gal->SetFontUnderlined(true);
|
||||||
|
|
||||||
|
// Visual: Draw styled "text" indicators
|
||||||
|
// Normal (N)
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.7, 0.7, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(240, 40), VECTOR2D(280, 80));
|
||||||
|
|
||||||
|
// Bold (B) - thicker
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.9, 0.9, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(300, 40), VECTOR2D(350, 80));
|
||||||
|
|
||||||
|
// Italic (I) - slanted parallelogram
|
||||||
|
std::deque<VECTOR2D> italic = {
|
||||||
|
VECTOR2D(380, 80),
|
||||||
|
VECTOR2D(370, 40),
|
||||||
|
VECTOR2D(410, 40),
|
||||||
|
VECTOR2D(420, 80)
|
||||||
|
};
|
||||||
|
gal->DrawPolygon(italic);
|
||||||
|
|
||||||
|
// Underlined (U)
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.7, 0.9, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(440, 40), VECTOR2D(480, 80));
|
||||||
|
gal->DrawRectangle(VECTOR2D(440, 85), VECTOR2D(480, 90)); // Underline
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(220, 20), VECTOR2D(500, 110));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 3: SetTextMirrored()
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Test mirroring API
|
||||||
|
gal->SetTextMirrored(false);
|
||||||
|
bool isMirrored = gal->IsTextMirrored(); // Should be false
|
||||||
|
|
||||||
|
gal->SetTextMirrored(true);
|
||||||
|
isMirrored = gal->IsTextMirrored(); // Should be true
|
||||||
|
|
||||||
|
// Visual: Show normal vs mirrored "F" shape
|
||||||
|
// Normal F
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.5, 0.8, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(560, 40), VECTOR2D(570, 100)); // Vertical
|
||||||
|
gal->DrawRectangle(VECTOR2D(570, 40), VECTOR2D(600, 50)); // Top horizontal
|
||||||
|
gal->DrawRectangle(VECTOR2D(570, 60), VECTOR2D(590, 70)); // Middle horizontal
|
||||||
|
|
||||||
|
// Mirrored F
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.5, 0.7, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(680, 40), VECTOR2D(690, 100)); // Vertical
|
||||||
|
gal->DrawRectangle(VECTOR2D(650, 40), VECTOR2D(680, 50)); // Top horizontal (mirrored)
|
||||||
|
gal->DrawRectangle(VECTOR2D(660, 60), VECTOR2D(680, 70)); // Middle horizontal (mirrored)
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.5, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(540, 20), VECTOR2D(720, 120));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 4: SetHorizontalJustify() / SetVerticalJustify()
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Test justify APIs
|
||||||
|
gal->SetHorizontalJustify(GR_TEXT_H_ALIGN_LEFT);
|
||||||
|
gal->SetVerticalJustify(GR_TEXT_V_ALIGN_TOP);
|
||||||
|
|
||||||
|
gal->SetHorizontalJustify(GR_TEXT_H_ALIGN_CENTER);
|
||||||
|
gal->SetVerticalJustify(GR_TEXT_V_ALIGN_CENTER);
|
||||||
|
|
||||||
|
gal->SetHorizontalJustify(GR_TEXT_H_ALIGN_RIGHT);
|
||||||
|
gal->SetVerticalJustify(GR_TEXT_V_ALIGN_BOTTOM);
|
||||||
|
|
||||||
|
// Visual: Show alignment positions
|
||||||
|
double alignBaseX = 120;
|
||||||
|
double alignBaseY = 200;
|
||||||
|
double boxSize = 100;
|
||||||
|
|
||||||
|
// Alignment box
|
||||||
|
gal->SetFillColor(COLOR4D(0.2, 0.2, 0.25, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(alignBaseX, alignBaseY),
|
||||||
|
VECTOR2D(alignBaseX + boxSize, alignBaseY + boxSize));
|
||||||
|
|
||||||
|
// Alignment indicators
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.6, 0.3, 1.0));
|
||||||
|
|
||||||
|
// Top-left
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + 10, alignBaseY + 10), 6);
|
||||||
|
// Top-center
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + boxSize/2, alignBaseY + 10), 6);
|
||||||
|
// Top-right
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + boxSize - 10, alignBaseY + 10), 6);
|
||||||
|
|
||||||
|
// Center-left
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + 10, alignBaseY + boxSize/2), 6);
|
||||||
|
// Center-center
|
||||||
|
gal->SetFillColor(COLOR4D(1.0, 0.8, 0.3, 1.0));
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + boxSize/2, alignBaseY + boxSize/2), 8);
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.6, 0.3, 1.0));
|
||||||
|
// Center-right
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + boxSize - 10, alignBaseY + boxSize/2), 6);
|
||||||
|
|
||||||
|
// Bottom-left
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + 10, alignBaseY + boxSize - 10), 6);
|
||||||
|
// Bottom-center
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + boxSize/2, alignBaseY + boxSize - 10), 6);
|
||||||
|
// Bottom-right
|
||||||
|
gal->DrawCircle(VECTOR2D(alignBaseX + boxSize - 10, alignBaseY + boxSize - 10), 6);
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.7, 0.5, 0.3, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(alignBaseX - 20, alignBaseY - 30),
|
||||||
|
VECTOR2D(alignBaseX + boxSize + 20, alignBaseY + boxSize + 20));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 5: ResetTextAttributes()
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
// Set various attributes
|
||||||
|
gal->SetGlyphSize(VECTOR2D(50, 60));
|
||||||
|
gal->SetFontBold(true);
|
||||||
|
gal->SetFontItalic(true);
|
||||||
|
gal->SetTextMirrored(true);
|
||||||
|
gal->SetHorizontalJustify(GR_TEXT_H_ALIGN_RIGHT);
|
||||||
|
|
||||||
|
// Reset all attributes to defaults
|
||||||
|
gal->ResetTextAttributes();
|
||||||
|
|
||||||
|
// After reset, attributes should be back to defaults
|
||||||
|
VECTOR2D resetSize = gal->GetGlyphSize(); // Should be default
|
||||||
|
bool resetBold = gal->IsFontBold(); // Should be false
|
||||||
|
bool resetItalic = gal->IsFontItalic(); // Should be false
|
||||||
|
|
||||||
|
// Visual: Show "reset" indicator
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.7, 0.3, 0.8));
|
||||||
|
gal->DrawCircle(VECTOR2D(350, 250), 30);
|
||||||
|
|
||||||
|
// Checkmark inside circle
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(4.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 1.0, 1.0, 1.0));
|
||||||
|
std::vector<VECTOR2D> check = {
|
||||||
|
VECTOR2D(335, 250),
|
||||||
|
VECTOR2D(345, 262),
|
||||||
|
VECTOR2D(368, 235)
|
||||||
|
};
|
||||||
|
gal->DrawPolyline(check);
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.6, 0.3, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(280, 180), VECTOR2D(420, 310));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 6: Comprehensive API coverage visual
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(40);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.2, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 340), VECTOR2D(760, 560));
|
||||||
|
|
||||||
|
// Show all text attribute concepts visually
|
||||||
|
double rowY = 380;
|
||||||
|
double colSpacing = 150;
|
||||||
|
|
||||||
|
// Column 1: Size variations
|
||||||
|
gal->SetFillColor(COLOR4D(0.5, 0.7, 0.9, 0.9));
|
||||||
|
double sizes[] = {8, 16, 24, 32};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
double s = sizes[i];
|
||||||
|
gal->DrawRectangle(VECTOR2D(40, rowY + i * 40),
|
||||||
|
VECTOR2D(40 + s * 2, rowY + i * 40 + s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Column 2: Style variations (bold = filled, italic = slant, underline = line below)
|
||||||
|
double col2X = 40 + colSpacing;
|
||||||
|
|
||||||
|
// Regular
|
||||||
|
gal->SetFillColor(COLOR4D(0.6, 0.6, 0.6, 0.9));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col2X, rowY), VECTOR2D(col2X + 40, rowY + 30));
|
||||||
|
|
||||||
|
// Bold (wider/heavier)
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.9, 0.9, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col2X, rowY + 45), VECTOR2D(col2X + 50, rowY + 80));
|
||||||
|
|
||||||
|
// Italic (parallelogram)
|
||||||
|
std::deque<VECTOR2D> italicShape = {
|
||||||
|
VECTOR2D(col2X + 10, rowY + 125),
|
||||||
|
VECTOR2D(col2X, rowY + 90),
|
||||||
|
VECTOR2D(col2X + 40, rowY + 90),
|
||||||
|
VECTOR2D(col2X + 50, rowY + 125)
|
||||||
|
};
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.7, 0.9, 0.9));
|
||||||
|
gal->DrawPolygon(italicShape);
|
||||||
|
|
||||||
|
// Underlined
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.9, 0.7, 0.9));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col2X, rowY + 140), VECTOR2D(col2X + 40, rowY + 165));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col2X, rowY + 170), VECTOR2D(col2X + 40, rowY + 175));
|
||||||
|
|
||||||
|
// Column 3: Mirror demonstration
|
||||||
|
double col3X = 40 + colSpacing * 2;
|
||||||
|
|
||||||
|
// Normal "R"
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.9, 0.9));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col3X, rowY), VECTOR2D(col3X + 10, rowY + 50));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col3X + 10, rowY), VECTOR2D(col3X + 35, rowY + 10));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col3X + 10, rowY + 20), VECTOR2D(col3X + 30, rowY + 30));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col3X + 25, rowY + 25), VECTOR2D(col3X + 40, rowY + 50));
|
||||||
|
|
||||||
|
// Mirrored "R"
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.6, 0.8, 0.9));
|
||||||
|
double mirrorX = col3X + 80;
|
||||||
|
gal->DrawRectangle(VECTOR2D(mirrorX + 30, rowY + 80), VECTOR2D(mirrorX + 40, rowY + 130));
|
||||||
|
gal->DrawRectangle(VECTOR2D(mirrorX + 5, rowY + 80), VECTOR2D(mirrorX + 30, rowY + 90));
|
||||||
|
gal->DrawRectangle(VECTOR2D(mirrorX + 10, rowY + 100), VECTOR2D(mirrorX + 30, rowY + 110));
|
||||||
|
gal->DrawRectangle(VECTOR2D(mirrorX, rowY + 105), VECTOR2D(mirrorX + 15, rowY + 130));
|
||||||
|
|
||||||
|
// Column 4: Justify grid
|
||||||
|
double col4X = 40 + colSpacing * 3;
|
||||||
|
double gridSize = 80;
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.25, 0.25, 0.3, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(col4X, rowY), VECTOR2D(col4X + gridSize, rowY + gridSize));
|
||||||
|
|
||||||
|
// 3x3 justify positions
|
||||||
|
gal->SetFillColor(COLOR4D(0.9, 0.7, 0.4, 1.0));
|
||||||
|
for (int row = 0; row < 3; row++) {
|
||||||
|
for (int col = 0; col < 3; col++) {
|
||||||
|
double px = col4X + 10 + col * 30;
|
||||||
|
double py = rowY + 10 + row * 30;
|
||||||
|
double radius = (row == 1 && col == 1) ? 8 : 5;
|
||||||
|
gal->DrawCircle(VECTOR2D(px, py), radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Column 5: Rotation demonstration
|
||||||
|
double col5X = 40 + colSpacing * 4;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
double angle = i * M_PI / 6; // 0, 30, 60, 90 degrees
|
||||||
|
double cx = col5X + 40;
|
||||||
|
double cy = rowY + 40 + i * 45;
|
||||||
|
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(cx, cy));
|
||||||
|
gal->Rotate(angle);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.6 + i * 0.1, 0.8 - i * 0.1, 0.5, 0.9));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-15, -8), VECTOR2D(15, 8));
|
||||||
|
|
||||||
|
gal->Restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 340), VECTOR2D(760, 560));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||
289
tests/gal-regression/scenarios/scenario_transform.cpp
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
/**
|
||||||
|
* Transform() API Documentation Scenario
|
||||||
|
*
|
||||||
|
* Tests GAL::Transform(const MATRIX3x3D& aTransformation)
|
||||||
|
*
|
||||||
|
* IMPORTANT FINDING: Transform() is DEAD CODE in KiCad!
|
||||||
|
*
|
||||||
|
* Research revealed that:
|
||||||
|
* 1. Transform() is declared in GAL base class with empty default implementation
|
||||||
|
* 2. Implemented in both OPENGL_GAL (glMultMatrixd) and CAIRO_GAL
|
||||||
|
* 3. NEVER called anywhere in the entire KiCad codebase
|
||||||
|
* 4. KiCad uses Rotate(), Translate(), Scale() instead - which work correctly
|
||||||
|
*
|
||||||
|
* Why Transform() doesn't produce visible output in OPENGL_GAL:
|
||||||
|
* - Uses glMultMatrixd() which affects the legacy GL_MODELVIEW matrix stack
|
||||||
|
* - OPENGL_GAL renders via VERTEX_MANAGER which has its own m_transform member
|
||||||
|
* - The two transformation systems are completely independent
|
||||||
|
*
|
||||||
|
* This scenario:
|
||||||
|
* 1. Calls Transform() API to verify it doesn't crash
|
||||||
|
* 2. Documents the limitation
|
||||||
|
* 3. Shows what the expected result WOULD be if it worked
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <math/matrix3x3.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace GALTest {
|
||||||
|
|
||||||
|
using KIGFX::COLOR4D;
|
||||||
|
using KIGFX::GAL;
|
||||||
|
|
||||||
|
void RenderTransformAPI(GAL* gal, int width, int height) {
|
||||||
|
gal->SetLayerDepth(100);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(0, 0), VECTOR2D(width, height));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 1: Transform() API call demonstration
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
|
||||||
|
// Create a 3x3 identity transformation matrix
|
||||||
|
MATRIX3x3D identity;
|
||||||
|
identity.SetIdentity();
|
||||||
|
|
||||||
|
// Call Transform() with identity - should be a no-op
|
||||||
|
// This verifies the API is callable without crashing
|
||||||
|
gal->Transform(identity);
|
||||||
|
|
||||||
|
// Create a rotation matrix (45 degrees)
|
||||||
|
MATRIX3x3D rotationMatrix;
|
||||||
|
rotationMatrix.SetIdentity();
|
||||||
|
double angle = 45.0 * M_PI / 180.0;
|
||||||
|
rotationMatrix.m_data[0][0] = cos(angle);
|
||||||
|
rotationMatrix.m_data[0][1] = -sin(angle);
|
||||||
|
rotationMatrix.m_data[1][0] = sin(angle);
|
||||||
|
rotationMatrix.m_data[1][1] = cos(angle);
|
||||||
|
|
||||||
|
// Call Transform() with rotation matrix
|
||||||
|
// NOTE: In OPENGL_GAL this affects GL_MODELVIEW but NOT the VERTEX_MANAGER
|
||||||
|
// So subsequent drawing will NOT be transformed
|
||||||
|
gal->Transform(rotationMatrix);
|
||||||
|
|
||||||
|
// Draw a shape - it will NOT be rotated because VERTEX_MANAGER ignores GL_MODELVIEW
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.3, 0.3, 0.5));
|
||||||
|
gal->DrawRectangle(VECTOR2D(100, 100), VECTOR2D(200, 150));
|
||||||
|
|
||||||
|
// Create a translation matrix
|
||||||
|
MATRIX3x3D translationMatrix;
|
||||||
|
translationMatrix.SetIdentity();
|
||||||
|
translationMatrix.m_data[0][2] = 50; // Translate X by 50
|
||||||
|
translationMatrix.m_data[1][2] = 30; // Translate Y by 30
|
||||||
|
|
||||||
|
// Call Transform() with translation
|
||||||
|
gal->Transform(translationMatrix);
|
||||||
|
|
||||||
|
// Draw another shape - also NOT translated
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.8, 0.3, 0.5));
|
||||||
|
gal->DrawRectangle(VECTOR2D(100, 180), VECTOR2D(200, 230));
|
||||||
|
|
||||||
|
// Reset with identity
|
||||||
|
gal->Transform(identity);
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.3, 0.3, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 20), VECTOR2D(300, 280));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 2: What Transform() SHOULD do (simulated with working APIs)
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Reference shape (no transform)
|
||||||
|
gal->SetFillColor(COLOR4D(0.5, 0.5, 0.5, 0.7));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 100), VECTOR2D(500, 150));
|
||||||
|
|
||||||
|
// Using working transforms (Rotate/Translate/Scale via VERTEX_MANAGER)
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(450, 200));
|
||||||
|
gal->Rotate(45.0 * M_PI / 180.0);
|
||||||
|
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.6, 0.9, 0.9));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-50, -25), VECTOR2D(50, 25));
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.3, 0.6, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(320, 20), VECTOR2D(580, 280));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 3: Matrix operations visualization
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.15, 0.18, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(600, 20), VECTOR2D(780, 280));
|
||||||
|
|
||||||
|
// Show matrix element positions visually (3x3 grid)
|
||||||
|
double matrixX = 640;
|
||||||
|
double matrixY = 60;
|
||||||
|
double cellSize = 40;
|
||||||
|
|
||||||
|
// Grid cells
|
||||||
|
for (int row = 0; row < 3; row++) {
|
||||||
|
for (int col = 0; col < 3; col++) {
|
||||||
|
double x = matrixX + col * cellSize;
|
||||||
|
double y = matrixY + row * cellSize;
|
||||||
|
|
||||||
|
// Diagonal elements (scale/rotation) in one color
|
||||||
|
if (row == col) {
|
||||||
|
gal->SetFillColor(COLOR4D(0.4, 0.6, 0.8, 0.8));
|
||||||
|
}
|
||||||
|
// Translation column (last column) in another
|
||||||
|
else if (col == 2 && row < 2) {
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.6, 0.4, 0.8));
|
||||||
|
}
|
||||||
|
// Other elements
|
||||||
|
else {
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.3, 0.35, 0.8));
|
||||||
|
}
|
||||||
|
|
||||||
|
gal->DrawRectangle(VECTOR2D(x, y), VECTOR2D(x + cellSize - 2, y + cellSize - 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame around matrix
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.6, 0.6, 0.7, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(matrixX - 5, matrixY - 5),
|
||||||
|
VECTOR2D(matrixX + cellSize * 3 + 5, matrixY + cellSize * 3 + 5));
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.5, 0.6, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(600, 20), VECTOR2D(780, 280));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 4: Why Transform() doesn't work - architecture diagram
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.15, 0.12, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 300), VECTOR2D(380, 580));
|
||||||
|
|
||||||
|
// "GL_MODELVIEW" box (what Transform affects)
|
||||||
|
gal->SetFillColor(COLOR4D(0.6, 0.3, 0.3, 0.7));
|
||||||
|
gal->DrawRectangle(VECTOR2D(50, 340), VECTOR2D(170, 400));
|
||||||
|
|
||||||
|
// "VERTEX_MANAGER" box (what actually renders)
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.6, 0.3, 0.7));
|
||||||
|
gal->DrawRectangle(VECTOR2D(50, 440), VECTOR2D(170, 500));
|
||||||
|
|
||||||
|
// Arrow from Transform to GL_MODELVIEW
|
||||||
|
gal->SetFillColor(COLOR4D(0.8, 0.4, 0.4, 0.9));
|
||||||
|
gal->DrawSegment(VECTOR2D(240, 340), VECTOR2D(175, 365), 3);
|
||||||
|
|
||||||
|
// X mark showing no connection to VERTEX_MANAGER
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(4.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.3, 0.3, 1.0));
|
||||||
|
gal->DrawLine(VECTOR2D(200, 430), VECTOR2D(220, 450));
|
||||||
|
gal->DrawLine(VECTOR2D(220, 430), VECTOR2D(200, 450));
|
||||||
|
|
||||||
|
// Arrow from Rotate/Scale/Translate to VERTEX_MANAGER
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetFillColor(COLOR4D(0.4, 0.8, 0.4, 0.9));
|
||||||
|
gal->DrawSegment(VECTOR2D(240, 520), VECTOR2D(175, 475), 3);
|
||||||
|
|
||||||
|
// "Transform()" label box
|
||||||
|
gal->SetFillColor(COLOR4D(0.5, 0.3, 0.3, 0.6));
|
||||||
|
gal->DrawRectangle(VECTOR2D(240, 320), VECTOR2D(350, 360));
|
||||||
|
|
||||||
|
// "Rotate/Scale/Translate" label box
|
||||||
|
gal->SetFillColor(COLOR4D(0.3, 0.5, 0.3, 0.6));
|
||||||
|
gal->DrawRectangle(VECTOR2D(240, 500), VECTOR2D(350, 540));
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.5, 0.3, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(20, 300), VECTOR2D(380, 580));
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
// Section 5: Cairo comparison (where Transform DOES work)
|
||||||
|
//=========================================================================
|
||||||
|
gal->SetLayerDepth(50);
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// Background panel
|
||||||
|
gal->SetFillColor(COLOR4D(0.12, 0.15, 0.15, 1.0));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 300), VECTOR2D(780, 580));
|
||||||
|
|
||||||
|
// OpenGL box (shows limitation)
|
||||||
|
gal->SetFillColor(COLOR4D(0.4, 0.25, 0.25, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(430, 350), VECTOR2D(560, 420));
|
||||||
|
|
||||||
|
// Cairo box (shows working)
|
||||||
|
gal->SetFillColor(COLOR4D(0.25, 0.4, 0.25, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(610, 350), VECTOR2D(740, 420));
|
||||||
|
|
||||||
|
// "OpenGL: NO-OP" indicator
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(3.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(1.0, 0.4, 0.4, 1.0));
|
||||||
|
gal->DrawLine(VECTOR2D(470, 380), VECTOR2D(520, 390));
|
||||||
|
gal->DrawLine(VECTOR2D(520, 380), VECTOR2D(470, 390));
|
||||||
|
|
||||||
|
// "Cairo: WORKS" indicator (checkmark)
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 1.0, 0.4, 1.0));
|
||||||
|
std::vector<VECTOR2D> check = {
|
||||||
|
VECTOR2D(650, 385),
|
||||||
|
VECTOR2D(670, 400),
|
||||||
|
VECTOR2D(710, 360)
|
||||||
|
};
|
||||||
|
gal->DrawPolyline(check);
|
||||||
|
|
||||||
|
// Show result shapes
|
||||||
|
gal->SetIsFill(true);
|
||||||
|
gal->SetIsStroke(false);
|
||||||
|
|
||||||
|
// OpenGL result (not transformed)
|
||||||
|
gal->SetFillColor(COLOR4D(0.7, 0.5, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(460, 460), VECTOR2D(530, 510));
|
||||||
|
|
||||||
|
// Cairo result (would be transformed)
|
||||||
|
gal->Save();
|
||||||
|
gal->Translate(VECTOR2D(675, 485));
|
||||||
|
gal->Rotate(30.0 * M_PI / 180.0);
|
||||||
|
gal->SetFillColor(COLOR4D(0.5, 0.7, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(-35, -25), VECTOR2D(35, 25));
|
||||||
|
gal->Restore();
|
||||||
|
|
||||||
|
// Section frame
|
||||||
|
gal->SetIsFill(false);
|
||||||
|
gal->SetIsStroke(true);
|
||||||
|
gal->SetLineWidth(2.0);
|
||||||
|
gal->SetStrokeColor(COLOR4D(0.4, 0.5, 0.5, 0.8));
|
||||||
|
gal->DrawRectangle(VECTOR2D(400, 300), VECTOR2D(780, 580));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GALTest
|
||||||