diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8807360..209ce15 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -160,6 +160,7 @@ set(solvespace_SOURCES render/render.cpp render/rendergl1.cpp render/render2d.cpp + render/rendercairo.cpp srf/boolean.cpp srf/curve.cpp srf/merge.cpp diff --git a/src/drawentity.cpp b/src/drawentity.cpp index d0f02e9..b8f157b 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -491,7 +491,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) { Canvas::Fill fill = {}; fill.layer = stroke.layer; - fill.zIndex = IsPoint() ? zIndex + 1 : 0; + fill.zIndex = IsPoint() ? 5 : 0; fill.color = stroke.color; Canvas::hFill hcf = canvas->GetFill(fill); diff --git a/src/render/render.h b/src/render/render.h index 621951d..6839832 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -270,6 +270,33 @@ public: void OutputBezierAsNonrationalCubic(const SBezier &b, hStroke hcs); }; +//----------------------------------------------------------------------------- +// 2d renderers. +//----------------------------------------------------------------------------- + +class CairoRenderer : public SurfaceRenderer { +public: + cairo_t *context; + // Renderer state. + struct { + hStroke hcs; + } current; + + CairoRenderer() : context(), current() {} + + void SelectStroke(hStroke hcs); + void MoveTo(Vector p); + void FinishPath(); + + bool CanOutputCurves() const override { return true; } + bool CanOutputTriangles() const override { return true; } + + void OutputStart() override; + void OutputBezier(const SBezier &b, hStroke hcs) override; + void OutputTriangle(const STriangle &tr) override; + void OutputEnd() override; +}; + //----------------------------------------------------------------------------- // 3d renderers. //----------------------------------------------------------------------------- diff --git a/src/render/rendercairo.cpp b/src/render/rendercairo.cpp new file mode 100644 index 0000000..39ce8e7 --- /dev/null +++ b/src/render/rendercairo.cpp @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// A rendering backend that draws on a Cairo surface. +// +// Copyright 2016 whitequark +//----------------------------------------------------------------------------- +#include +#include "solvespace.h" + +namespace SolveSpace { + +void CairoRenderer::OutputStart() { + cairo_save(context); + + RgbaColor bgColor = lighting.backgroundColor; + cairo_rectangle(context, 0.0, 0.0, camera.width, camera.height); + cairo_set_source_rgba(context, bgColor.redF(), bgColor.greenF(), bgColor.blueF(), + bgColor.alphaF()); + cairo_fill(context); + + cairo_translate(context, camera.width / 2.0, camera.height / 2.0); + + cairo_set_line_join(context, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_cap(context, CAIRO_LINE_CAP_ROUND); +} + +void CairoRenderer::OutputEnd() { + FinishPath(); + + cairo_restore(context); + cairo_surface_flush(cairo_get_target(context)); +} + +void CairoRenderer::SelectStroke(hStroke hcs) { + if(current.hcs.v == hcs.v) return; + FinishPath(); + + Stroke *stroke = strokes.FindById(hcs); + current.hcs = hcs; + + RgbaColor color = stroke->color; + std::vector dashes = + StipplePatternDashes(stroke->stipplePattern, stroke->stippleScale * camera.scale); + cairo_set_line_width(context, stroke->width); + cairo_set_dash(context, dashes.data(), dashes.size(), 0); + cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(), + color.alphaF()); + cairo_set_antialias(context, CAIRO_ANTIALIAS_BEST); +} + +void CairoRenderer::MoveTo(Vector p) { + Point2d pos; + cairo_get_current_point(context, &pos.x, &pos.y); + if(cairo_has_current_point(context) && pos.Equals(p.ProjectXy())) return; + FinishPath(); + + cairo_move_to(context, p.x, p.y); +} + +void CairoRenderer::FinishPath() { + if(!cairo_has_current_point(context)) return; + + cairo_stroke(context); +} + +void CairoRenderer::OutputBezier(const SBezier &b, hStroke hcs) { + SelectStroke(hcs); + + Vector c, n = Vector::From(0, 0, 1); + double r; + if(b.deg == 1) { + MoveTo(b.ctrl[0]); + cairo_line_to(context, + b.ctrl[1].x, b.ctrl[1].y); + } else if(b.IsCircle(n, &c, &r)) { + MoveTo(b.ctrl[0]); + double theta0 = atan2(b.ctrl[0].y - c.y, b.ctrl[0].x - c.x), + theta1 = atan2(b.ctrl[2].y - c.y, b.ctrl[2].x - c.x), + dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI); + if(dtheta > 0) { + cairo_arc(context, + c.x, c.y, r, theta0, theta1); + } else { + cairo_arc_negative(context, + c.x, c.y, r, theta0, theta1); + } + } else if(b.deg == 3 && !b.IsRational()) { + MoveTo(b.ctrl[0]); + cairo_curve_to(context, + b.ctrl[1].x, b.ctrl[1].y, + b.ctrl[2].x, b.ctrl[2].y, + b.ctrl[3].x, b.ctrl[3].y); + } else { + OutputBezierAsNonrationalCubic(b, hcs); + } +} + +void CairoRenderer::OutputTriangle(const STriangle &tr) { + FinishPath(); + current.hcs = {}; + + RgbaColor color = tr.meta.color; + cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(), + color.alphaF()); + cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE); + cairo_move_to(context, tr.a.x, tr.a.y); + cairo_line_to(context, tr.b.x, tr.b.y); + cairo_line_to(context, tr.c.x, tr.c.y); + cairo_fill(context); +} + +} diff --git a/src/solvespace.h b/src/solvespace.h index ebdba26..fe68bc3 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -35,6 +35,8 @@ struct FT_LibraryRec_; struct FT_FaceRec_; +typedef struct _cairo cairo_t; + // The few floating-point equality comparisons in SolveSpace have been // carefully considered, so we disable the -Wfloat-equal warning for them #ifdef __clang__