2020-08-08 15:47:34 +02:00
|
|
|
/* File: draw.c
|
2020-10-12 13:13:31 +02:00
|
|
|
*
|
2020-08-08 15:47:34 +02:00
|
|
|
* This file is part of XSCHEM,
|
2020-10-12 13:13:31 +02:00
|
|
|
* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit
|
2020-08-08 15:47:34 +02:00
|
|
|
* simulation.
|
2024-11-12 20:23:18 +01:00
|
|
|
* Copyright (C) 1998-2024 Stefan Frederik Schippers
|
2020-08-08 15:47:34 +02:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "xschem.h"
|
|
|
|
|
|
2020-09-04 02:25:45 +02:00
|
|
|
|
|
|
|
|
#define xDashType LineOnOffDash
|
2022-12-01 16:30:02 +01:00
|
|
|
/* CapNotLast, CapButt, CapRound or CapProjecting */
|
|
|
|
|
#define xCap CapNotLast
|
|
|
|
|
/* JoinMiter, JoinRound, or JoinBevel */
|
|
|
|
|
#define xJoin JoinBevel
|
2022-11-30 00:59:45 +01:00
|
|
|
|
2023-04-09 15:36:06 +02:00
|
|
|
#if !defined(__unix__) && HAS_CAIRO==1
|
2022-03-01 19:36:08 +01:00
|
|
|
static void clear_cairo_surface(cairo_t *cr, double x, double y, double width, double height)
|
|
|
|
|
{
|
|
|
|
|
cairo_save(cr);
|
|
|
|
|
cairo_set_source_rgba(cr, 0, 0, 0, 0);
|
|
|
|
|
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
|
cairo_rectangle(cr, x, y, width, height);
|
|
|
|
|
cairo_fill(cr);
|
|
|
|
|
/*cairo_paint(cr);
|
|
|
|
|
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);*/
|
|
|
|
|
cairo_restore(cr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void my_cairo_fill(cairo_surface_t *src_surface, int x, int y, unsigned int width, unsigned int height)
|
|
|
|
|
{
|
|
|
|
|
HWND hwnd = Tk_GetHWND(xctx->window);
|
|
|
|
|
HDC dc = GetDC(hwnd);
|
|
|
|
|
cairo_surface_t *dest_surface = cairo_win32_surface_create(dc);
|
|
|
|
|
if (cairo_surface_status(dest_surface) != CAIRO_STATUS_SUCCESS) {
|
|
|
|
|
fprintf(errfp, "ERROR: invalid cairo surface to copy over\n");
|
|
|
|
|
}
|
|
|
|
|
cairo_t *ct = cairo_create(dest_surface);
|
|
|
|
|
cairo_surface_flush(src_surface);
|
|
|
|
|
cairo_set_source_surface(ct, src_surface, 0, 0);
|
|
|
|
|
cairo_rectangle(ct, x, y, width, height);
|
|
|
|
|
cairo_set_operator(ct, CAIRO_OPERATOR_ADD);
|
|
|
|
|
cairo_fill(ct);
|
|
|
|
|
cairo_destroy(ct); ct = NULL;
|
|
|
|
|
cairo_surface_destroy(dest_surface); dest_surface = NULL;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2025-05-20 02:51:52 +02:00
|
|
|
#ifdef __unix__
|
2025-04-20 09:48:40 +02:00
|
|
|
int xserver_ok(void)
|
|
|
|
|
{
|
|
|
|
|
int has_x = 1;
|
|
|
|
|
if(!getenv("DISPLAY") || !getenv("DISPLAY")[0]) has_x = 0;
|
|
|
|
|
else {
|
|
|
|
|
display = XOpenDisplay(NULL);
|
|
|
|
|
if(!display) {
|
|
|
|
|
has_x=0;
|
|
|
|
|
fprintf(errfp, "\n X server connection failed, although DISPLAY shell variable is set.\n"
|
|
|
|
|
" A possible reason is that the X server is not running or DISPLAY shell variable\n"
|
|
|
|
|
" is incorrectly set.\n"
|
|
|
|
|
" Starting Xschem in text only mode.\n\n");
|
|
|
|
|
} else XCloseDisplay(display);
|
|
|
|
|
}
|
|
|
|
|
return has_x;
|
|
|
|
|
}
|
2025-05-20 02:51:52 +02:00
|
|
|
#endif
|
2025-04-20 09:48:40 +02:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
int textclip(int x1,int y1,int x2,int y2,
|
|
|
|
|
double xa,double ya,double xb,double yb)
|
|
|
|
|
/* check if some of (xa,ya-xb,yb) is inside (x1,y1-x2,y2) */
|
|
|
|
|
/* coordinates should be ordered, x1<x2,ya<yb and so on... */
|
|
|
|
|
{
|
2022-12-13 13:51:33 +01:00
|
|
|
/*
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(2, "textclip(): %.16g %.16g %.16g %.16g - %d %d %d %d\n",
|
|
|
|
|
X_TO_SCREEN(xa),Y_TO_SCREEN(ya), X_TO_SCREEN(xb),Y_TO_SCREEN(yb),x1,y1,x2,y2);
|
2022-12-13 13:51:33 +01:00
|
|
|
*/
|
2021-11-16 22:28:10 +01:00
|
|
|
/* drawtemprect(xctx->gc[WIRELAYER],xa,ya,xb,yb); */
|
2020-08-08 15:47:34 +02:00
|
|
|
if (X_TO_SCREEN(xa)>x2) return 0;
|
|
|
|
|
else if (Y_TO_SCREEN(ya)>y2) return 0;
|
|
|
|
|
else if (X_TO_SCREEN(xb)<x1) return 0;
|
|
|
|
|
else if (Y_TO_SCREEN(yb)<y1) return 0;
|
|
|
|
|
return 1;
|
2020-10-12 13:13:31 +02:00
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2020-12-22 00:13:25 +01:00
|
|
|
void print_image()
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2022-01-26 15:20:00 +01:00
|
|
|
#if HAS_CAIRO == 0
|
2020-08-08 15:47:34 +02:00
|
|
|
char cmd[PATH_MAX+100];
|
2022-01-26 15:20:00 +01:00
|
|
|
#endif
|
2023-04-27 08:49:52 +02:00
|
|
|
int save, save_draw_grid, save_draw_window;
|
2020-12-27 22:59:15 +01:00
|
|
|
static char lastdir[PATH_MAX] = "";
|
2020-08-08 15:47:34 +02:00
|
|
|
const char *r;
|
|
|
|
|
|
2022-12-13 13:51:33 +01:00
|
|
|
if(!has_x) return;
|
2020-12-27 22:59:15 +01:00
|
|
|
if(!lastdir[0]) my_strncpy(lastdir, pwd_dir, S(lastdir));
|
2021-07-14 01:43:29 +02:00
|
|
|
if(!xctx->plotfile[0]) {
|
2023-08-30 00:42:09 +02:00
|
|
|
/* tclvareval("tk_getSaveFile -title {Select destination file} -initialfile {",
|
|
|
|
|
* get_cell(xctx->sch[xctx->currsch], 0), ".png} -initialdir {", lastdir, "}", NULL); */
|
|
|
|
|
tclvareval("save_file_dialog {Select destination file} *.png INITIALLOADDIR {", pwd_dir, "/",
|
|
|
|
|
get_cell(xctx->sch[xctx->currsch], 0), ".png}", NULL);
|
2020-08-16 03:34:45 +02:00
|
|
|
r = tclresult();
|
2020-12-27 22:59:15 +01:00
|
|
|
if(r[0]) {
|
2021-07-14 01:43:29 +02:00
|
|
|
my_strncpy(xctx->plotfile, r, S(xctx->plotfile));
|
2021-12-05 12:39:05 +01:00
|
|
|
tclvareval("file dirname {", xctx->plotfile, "}", NULL);
|
2020-12-27 22:59:15 +01:00
|
|
|
my_strncpy(lastdir, tclresult(), S(lastdir));
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
else return;
|
|
|
|
|
}
|
2021-11-10 13:43:08 +01:00
|
|
|
save_draw_grid = tclgetboolvar("draw_grid");
|
|
|
|
|
tclsetvar("draw_grid", "0");
|
2022-09-15 19:39:16 +02:00
|
|
|
save_draw_window = xctx->draw_window;
|
|
|
|
|
xctx->draw_window=0;
|
2021-10-26 00:04:13 +02:00
|
|
|
xctx->draw_pixmap=1;
|
2023-04-27 08:49:52 +02:00
|
|
|
save = xctx->do_copy_area;
|
2022-09-15 19:39:16 +02:00
|
|
|
xctx->do_copy_area=0;
|
2020-08-08 15:47:34 +02:00
|
|
|
draw();
|
2022-02-02 00:11:46 +01:00
|
|
|
|
2022-01-26 15:20:00 +01:00
|
|
|
|
|
|
|
|
#if HAS_CAIRO == 1 /* use cairo native support for png writing, no need to convert
|
|
|
|
|
* XPM and handles Xrender extensions for transparent embedded images */
|
|
|
|
|
{
|
|
|
|
|
cairo_surface_t *png_sfc;
|
2022-09-15 19:39:16 +02:00
|
|
|
#ifdef __unix__
|
2022-01-26 15:20:00 +01:00
|
|
|
png_sfc = cairo_xlib_surface_create(display, xctx->save_pixmap, visual,
|
|
|
|
|
xctx->xrect[0].width, xctx->xrect[0].height);
|
2022-09-15 19:39:16 +02:00
|
|
|
#else
|
2022-02-02 00:11:46 +01:00
|
|
|
HWND hwnd = Tk_GetHWND(xctx->window);
|
|
|
|
|
HDC dc = GetDC(hwnd);
|
|
|
|
|
png_sfc = cairo_win32_surface_create(dc);
|
2022-09-15 19:39:16 +02:00
|
|
|
#endif
|
2022-02-02 00:11:46 +01:00
|
|
|
|
2022-01-26 15:20:00 +01:00
|
|
|
if(xctx->plotfile[0])
|
|
|
|
|
cairo_surface_write_to_png(png_sfc, xctx->plotfile);
|
|
|
|
|
else
|
|
|
|
|
cairo_surface_write_to_png(png_sfc, "plot.png");
|
2022-09-15 19:39:16 +02:00
|
|
|
|
|
|
|
|
cairo_surface_destroy(png_sfc);
|
2022-01-26 15:20:00 +01:00
|
|
|
}
|
|
|
|
|
#else /* no cairo */
|
2022-09-15 19:39:16 +02:00
|
|
|
#ifdef __unix__
|
2024-11-25 14:29:19 +01:00
|
|
|
XpmWriteFileFromPixmap(display, "plot.xpm", xctx->save_pixmap, 0, NULL ); /* .gz ???? */
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(1, "print_image(): Window image saved\n");
|
2021-07-14 01:43:29 +02:00
|
|
|
if(xctx->plotfile[0]) {
|
|
|
|
|
my_snprintf(cmd, S(cmd), "convert_to_png plot.xpm {%s}", xctx->plotfile);
|
2020-10-12 13:13:31 +02:00
|
|
|
tcleval(cmd);
|
|
|
|
|
} else tcleval( "convert_to_png plot.xpm plot.png");
|
2022-09-15 19:39:16 +02:00
|
|
|
#else
|
2022-02-02 00:11:46 +01:00
|
|
|
char *psfile = NULL;
|
2023-12-12 01:14:40 +01:00
|
|
|
create_ps(&psfile, 7, 0, 0);
|
2022-02-02 00:11:46 +01:00
|
|
|
if (xctx->plotfile[0]) {
|
2021-07-14 01:43:29 +02:00
|
|
|
my_snprintf(cmd, S(cmd), "convert_to_png {%s} {%s}", psfile, xctx->plotfile);
|
2021-01-28 23:09:37 +01:00
|
|
|
tcleval(cmd);
|
2022-02-02 00:11:46 +01:00
|
|
|
}
|
2024-06-09 23:38:20 +02:00
|
|
|
else tclvareval("convert_to_png {", psfile, "} plot.png", NULL);
|
2022-09-15 19:39:16 +02:00
|
|
|
#endif
|
2021-01-28 23:09:37 +01:00
|
|
|
#endif
|
2021-07-14 01:43:29 +02:00
|
|
|
my_strncpy(xctx->plotfile,"", S(xctx->plotfile));
|
2021-11-10 13:43:08 +01:00
|
|
|
tclsetboolvar("draw_grid", save_draw_grid);
|
2021-10-26 00:04:13 +02:00
|
|
|
xctx->draw_pixmap=1;
|
2022-09-15 19:39:16 +02:00
|
|
|
xctx->draw_window=save_draw_window;
|
2023-04-27 08:49:52 +02:00
|
|
|
xctx->do_copy_area=save;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-20 09:48:40 +02:00
|
|
|
#if defined(__unix__) && HAS_CAIRO==1
|
|
|
|
|
int grabscreen(const char *win_path, int event, int mx, int my, KeySym key,
|
|
|
|
|
int button, int aux, int state)
|
|
|
|
|
{
|
|
|
|
|
static int grab_state = 0;
|
|
|
|
|
static int x1, y1, x2, y2;
|
|
|
|
|
int rmx, rmy, wmx, wmy;
|
|
|
|
|
unsigned int msq;
|
|
|
|
|
Window rw, cw;
|
|
|
|
|
XSetWindowAttributes winattr;
|
|
|
|
|
XGCValues gcv;
|
|
|
|
|
static GC gc = NULL;
|
|
|
|
|
static Window clientwin = 0;
|
|
|
|
|
static int first_motion = 1;
|
|
|
|
|
static int displayh = 0, displayw = 0;
|
|
|
|
|
static unsigned long white = 0;
|
|
|
|
|
|
|
|
|
|
if(grab_state == 0 && event == ButtonPress && button == Button1) {
|
|
|
|
|
unsigned long gcvm = GCFunction | GCForeground;
|
|
|
|
|
|
|
|
|
|
white = WhitePixel(display, screen_number);
|
|
|
|
|
displayh = DisplayHeight(display, screen_number);
|
|
|
|
|
displayw = DisplayWidth(display, screen_number);
|
|
|
|
|
|
|
|
|
|
XQueryPointer(display, xctx->window, &rw, &cw , &rmx, &rmy, &wmx, &wmy, &msq);
|
|
|
|
|
gcv.function = GXxor;
|
|
|
|
|
gcv.foreground = white;
|
|
|
|
|
gc = XCreateGC(display, rw, gcvm, &gcv);
|
|
|
|
|
|
|
|
|
|
winattr.override_redirect = True;
|
|
|
|
|
clientwin = XCreateWindow(display, rw, 0, 0, displayw, displayh, 0, screendepth,
|
|
|
|
|
InputOutput, visual, CWOverrideRedirect, &winattr);
|
|
|
|
|
XMapRaised(display,clientwin);
|
|
|
|
|
|
|
|
|
|
x1 = rmx;
|
|
|
|
|
y1 = rmy;
|
|
|
|
|
dbg(1, "grabscreen(): got point1: %d %d\n", x1, y1);
|
|
|
|
|
grab_state = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(grab_state == 1 && event == MotionNotify) {
|
|
|
|
|
static int xx1, xx2, yy1, yy2;
|
|
|
|
|
xx1 = x1; yy1 = y1; xx2 = x2; yy2 = y2;
|
|
|
|
|
INT_RECTORDER(xx1, yy1, xx2, yy2);
|
|
|
|
|
dbg(1, "Motion: %d %d %d %d\n", xx1, yy1, xx2, yy2);
|
|
|
|
|
if(!first_motion) {
|
|
|
|
|
XDrawRectangle(display, clientwin, gc, xx1 - 1, yy1 - 1, xx2 - xx1 + 2, yy2 - yy1 + 2);
|
|
|
|
|
}
|
|
|
|
|
first_motion = 0;
|
|
|
|
|
XQueryPointer(display, xctx->window, &rw, &cw , &rmx, &rmy, &wmx, &wmy, &msq);
|
|
|
|
|
x2 = xx2 = rmx;
|
|
|
|
|
y2 = yy2 = rmy;
|
|
|
|
|
xx1 = x1; yy1 = y1;
|
|
|
|
|
INT_RECTORDER(xx1, yy1, xx2, yy2);
|
|
|
|
|
XDrawRectangle(display, clientwin, gc, xx1 - 1, yy1 - 1, xx2 - xx1 + 2, yy2 - yy1 + 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(grab_state == 1 && event == ButtonRelease) {
|
|
|
|
|
int grab_w = 0, grab_h = 0;
|
|
|
|
|
cairo_surface_t *sfc = NULL, *subsfc = NULL;
|
|
|
|
|
png_to_byte_closure_t closure;
|
|
|
|
|
char *encoded_data = NULL;
|
|
|
|
|
size_t olength;
|
|
|
|
|
char *prop = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grab_state = 0;
|
|
|
|
|
first_motion = 1;
|
|
|
|
|
xctx->ui_state &= ~GRABSCREEN;
|
|
|
|
|
XQueryPointer(display, xctx->window, &rw, &cw , &rmx, &rmy, &wmx, &wmy, &msq);
|
|
|
|
|
x2 = rmx;
|
|
|
|
|
y2 = rmy;
|
|
|
|
|
INT_RECTORDER(x1, y1, x2, y2);
|
|
|
|
|
tclvareval("grab release ", xctx->top_path, ".drw", NULL);
|
|
|
|
|
if(x2 - x1 > 10 && y2 -y1 > 10) {
|
|
|
|
|
xctx->push_undo();
|
|
|
|
|
grab_w = (x2 - x1 + 1);
|
|
|
|
|
grab_h = (y2 - y1 + 1);
|
|
|
|
|
dbg(1, "grabscreen(): grab area: %d %d - %d %d\n", x1, y1, x2, y2);
|
|
|
|
|
dbg(1, "grabscreen(): root w=%d, h=%d\n", displayw, displayh);
|
|
|
|
|
sfc = cairo_xlib_surface_create(display, rw, visual, displayw, displayh);
|
|
|
|
|
if(!sfc || cairo_surface_status(sfc) != CAIRO_STATUS_SUCCESS) {
|
|
|
|
|
dbg(0, "grabscreen(): failure creating sfc\n");
|
|
|
|
|
XFreeGC(display, gc);
|
|
|
|
|
XDestroyWindow(display, clientwin);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
dbg(1, "sfc: w=%d, h=%d\n",
|
|
|
|
|
cairo_xlib_surface_get_width(sfc),
|
|
|
|
|
cairo_xlib_surface_get_height(sfc));
|
|
|
|
|
subsfc = cairo_surface_create_for_rectangle(sfc, x1, y1, grab_w, grab_h);
|
|
|
|
|
if(!subsfc || cairo_surface_status(subsfc) != CAIRO_STATUS_SUCCESS) {
|
|
|
|
|
dbg(0, "grabscreen(): failure creating subsfc\n");
|
|
|
|
|
cairo_surface_destroy(sfc);
|
|
|
|
|
XFreeGC(display, gc);
|
|
|
|
|
XDestroyWindow(display, clientwin);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
closure.buffer = NULL;
|
|
|
|
|
closure.size = 0;
|
|
|
|
|
closure.pos = 0;
|
|
|
|
|
cairo_surface_write_to_png_stream(subsfc, png_writer, &closure);
|
|
|
|
|
cairo_surface_destroy(subsfc);
|
|
|
|
|
cairo_surface_destroy(sfc);
|
|
|
|
|
closure.size = closure.pos;
|
|
|
|
|
dbg(1, "closure.size = %ld\n", closure.size);
|
|
|
|
|
encoded_data = base64_encode((unsigned char *)closure.buffer, closure.size, &olength, 0);
|
|
|
|
|
dbg(1, "olength = %ld\n", olength);
|
|
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
|
|
|
|
my_mstrcat(_ALLOC_ID_, &prop, "flags=image,unscaled\nalpha=0.8\nimage_data=", encoded_data, NULL);
|
|
|
|
|
my_free(_ALLOC_ID_, &encoded_data);
|
|
|
|
|
storeobject(-1, xctx->mousex_snap, xctx->mousey_snap, xctx->mousex_snap + grab_w, xctx->mousey_snap + grab_h,
|
|
|
|
|
xRECT, GRIDLAYER, SELECTED, prop);
|
|
|
|
|
my_free(_ALLOC_ID_, &prop);
|
|
|
|
|
xctx->need_reb_sel_arr=1;
|
|
|
|
|
rebuild_selected_array();
|
|
|
|
|
move_objects(START,0,0,0);
|
|
|
|
|
xctx->ui_state |= START_SYMPIN;
|
|
|
|
|
}
|
|
|
|
|
XFreeGC(display, gc);
|
|
|
|
|
XDestroyWindow(display, clientwin);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-12-07 20:04:57 +01:00
|
|
|
#if HAS_CAIRO==1
|
2022-02-19 14:31:55 +01:00
|
|
|
static void set_cairo_color(int layer)
|
2020-11-19 15:08:40 +01:00
|
|
|
{
|
2020-12-06 02:10:53 +01:00
|
|
|
cairo_set_source_rgb(xctx->cairo_ctx,
|
2021-11-16 22:28:10 +01:00
|
|
|
(double)xctx->xcolor_array[layer].red/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].green/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].blue/65535.0);
|
2020-12-03 18:21:23 +01:00
|
|
|
cairo_set_source_rgb(xctx->cairo_save_ctx,
|
2021-11-16 22:28:10 +01:00
|
|
|
(double)xctx->xcolor_array[layer].red/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].green/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].blue/65535.0);
|
2020-11-19 15:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-06 02:10:53 +01:00
|
|
|
/* remember to call cairo_restore(xctx->cairo_ctx) when done !! */
|
2020-10-12 13:13:31 +02:00
|
|
|
int set_text_custom_font(xText *txt) /* 20171122 for correct text_bbox calculation */
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2021-11-10 13:43:08 +01:00
|
|
|
const char *textfont;
|
2022-09-08 20:58:12 +02:00
|
|
|
if (xctx->cairo_ctx==NULL) return 0;
|
2020-08-08 15:47:34 +02:00
|
|
|
textfont = txt->font;
|
2022-01-13 12:46:55 +01:00
|
|
|
if((textfont && textfont[0]) || (txt->flags & (TEXT_BOLD | TEXT_OBLIQUE | TEXT_ITALIC))) {
|
2020-09-23 18:15:26 +02:00
|
|
|
cairo_font_slant_t slant;
|
|
|
|
|
cairo_font_weight_t weight;
|
2021-11-10 13:43:08 +01:00
|
|
|
textfont = (txt->font && txt->font[0]) ? txt->font : tclgetvar("cairo_font_name");
|
2020-09-23 18:15:26 +02:00
|
|
|
weight = ( txt->flags & TEXT_BOLD) ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL;
|
|
|
|
|
slant = CAIRO_FONT_SLANT_NORMAL;
|
|
|
|
|
if(txt->flags & TEXT_ITALIC) slant = CAIRO_FONT_SLANT_ITALIC;
|
|
|
|
|
if(txt->flags & TEXT_OBLIQUE) slant = CAIRO_FONT_SLANT_OBLIQUE;
|
2020-12-06 02:10:53 +01:00
|
|
|
cairo_save(xctx->cairo_ctx);
|
2022-12-25 10:42:07 +01:00
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create(textfont, slant, weight);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
2022-12-25 23:13:59 +01:00
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
2020-08-08 15:47:34 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#else
|
2020-10-12 13:13:31 +02:00
|
|
|
int set_text_custom_font(xText *txt)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2020-12-07 20:04:57 +01:00
|
|
|
#if HAS_CAIRO==1
|
2020-12-03 18:21:23 +01:00
|
|
|
static void cairo_draw_string_line(cairo_t *c_ctx, char *s,
|
2020-12-05 03:16:01 +01:00
|
|
|
double x, double y, short rot, short flip,
|
2020-12-23 05:07:39 +01:00
|
|
|
int lineno, double fontheight, double fontascent, double fontdescent,
|
2022-04-28 00:00:51 +02:00
|
|
|
int llength, int no_of_lines, double longest_line)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
double ix, iy;
|
2020-12-05 03:16:01 +01:00
|
|
|
short rot1;
|
2022-04-27 13:18:45 +02:00
|
|
|
double line_delta;
|
2020-08-08 15:47:34 +02:00
|
|
|
double lines;
|
|
|
|
|
double vc; /* 20171121 vert correct */
|
2021-11-10 13:43:08 +01:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
if(s==NULL) return;
|
|
|
|
|
if(llength==0) return;
|
|
|
|
|
|
2022-04-27 13:18:45 +02:00
|
|
|
line_delta = (lineno*fontheight*cairo_font_line_spacing);
|
2020-12-23 05:07:39 +01:00
|
|
|
lines = (no_of_lines-1)*fontheight*cairo_font_line_spacing;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
ix=X_TO_SCREEN(x);
|
|
|
|
|
iy=Y_TO_SCREEN(y);
|
|
|
|
|
if(rot&1) {
|
|
|
|
|
rot1=3;
|
|
|
|
|
} else rot1=0;
|
|
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
vc = cairo_vert_correct*xctx->mooz; /* converted to device (pixel) space */
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
if( rot==0 && flip==0) {iy+=line_delta+fontascent-vc;}
|
2020-12-23 05:07:39 +01:00
|
|
|
else if(rot==1 && flip==0) {iy+=longest_line;ix=ix-fontheight+fontascent+vc-lines+line_delta;}
|
|
|
|
|
else if(rot==2 && flip==0) {iy=iy-fontheight-lines+line_delta+fontascent+vc; ix=ix-longest_line;}
|
2020-08-08 15:47:34 +02:00
|
|
|
else if(rot==3 && flip==0) {ix+=line_delta+fontascent-vc;}
|
2020-12-23 05:07:39 +01:00
|
|
|
else if(rot==0 && flip==1) {ix=ix-longest_line;iy+=line_delta+fontascent-vc;}
|
2020-08-08 15:47:34 +02:00
|
|
|
else if(rot==1 && flip==1) {ix=ix-fontheight+line_delta-lines+fontascent+vc;}
|
|
|
|
|
else if(rot==2 && flip==1) {iy=iy-fontheight-lines+line_delta+fontascent+vc;}
|
2020-12-23 05:07:39 +01:00
|
|
|
else if(rot==3 && flip==1) {iy=iy+longest_line;ix+=line_delta+fontascent-vc;}
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2020-12-03 18:21:23 +01:00
|
|
|
cairo_save(c_ctx);
|
|
|
|
|
cairo_translate(c_ctx, ix, iy);
|
|
|
|
|
cairo_rotate(c_ctx, XSCH_PI/2*rot1);
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2024-11-25 14:29:19 +01:00
|
|
|
cairo_move_to(c_ctx, 0, 0);
|
2020-12-03 18:21:23 +01:00
|
|
|
cairo_show_text(c_ctx, s);
|
|
|
|
|
cairo_restore(c_ctx);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* CAIRO version */
|
2020-12-05 03:16:01 +01:00
|
|
|
void draw_string(int layer, int what, const char *str, short rot, short flip, int hcenter, int vcenter,
|
2020-08-24 08:36:47 +02:00
|
|
|
double x, double y, double xscale, double yscale)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2021-11-18 01:55:01 +01:00
|
|
|
double textx1,textx2,texty1,texty2;
|
2020-08-31 02:25:41 +02:00
|
|
|
char *tt, *ss, *sss=NULL;
|
2020-08-08 15:47:34 +02:00
|
|
|
char c;
|
2020-10-12 13:13:31 +02:00
|
|
|
int lineno=0;
|
2020-08-08 15:47:34 +02:00
|
|
|
double size;
|
2024-05-23 00:57:45 +02:00
|
|
|
char *estr = NULL; /* expanded str: tabs replaced with spaces */
|
2020-08-08 15:47:34 +02:00
|
|
|
cairo_font_extents_t fext;
|
2022-04-28 00:00:51 +02:00
|
|
|
int llength=0, no_of_lines;
|
|
|
|
|
double longest_line;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
(void)what; /* UNUSED in cairo version, avoid compiler warning */
|
2020-11-18 12:46:57 +01:00
|
|
|
if(str==NULL || !has_x ) return;
|
2020-08-08 15:47:34 +02:00
|
|
|
size = xscale*52.*cairo_font_scale;
|
2020-10-15 17:39:21 +02:00
|
|
|
/*fprintf(errfp, "size=%.16g\n", size*xctx->mooz); */
|
|
|
|
|
if(size*xctx->mooz<3.0) return; /* too small */
|
|
|
|
|
if(size*xctx->mooz>1600) return; /* too big */
|
2024-05-23 00:57:45 +02:00
|
|
|
estr = my_expand(str, tclgetintvar("tabstop"));
|
|
|
|
|
text_bbox(estr, xscale, yscale, rot, flip, hcenter, vcenter, x,y,
|
2020-12-23 05:07:39 +01:00
|
|
|
&textx1,&texty1,&textx2,&texty2, &no_of_lines, &longest_line);
|
|
|
|
|
if(!textclip(xctx->areax1,xctx->areay1,xctx->areax2,
|
|
|
|
|
xctx->areay2,textx1,texty1,textx2,texty2)) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &estr);
|
2020-08-08 15:47:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-11-18 18:29:14 +01:00
|
|
|
|
2020-08-24 15:23:37 +02:00
|
|
|
if(hcenter) {
|
|
|
|
|
if(rot == 0 && flip == 0 ) { x=textx1;}
|
|
|
|
|
if(rot == 1 && flip == 0 ) { y=texty1;}
|
|
|
|
|
if(rot == 2 && flip == 0 ) { x=textx2;}
|
|
|
|
|
if(rot == 3 && flip == 0 ) { y=texty2;}
|
|
|
|
|
if(rot == 0 && flip == 1 ) { x=textx2;}
|
|
|
|
|
if(rot == 1 && flip == 1 ) { y=texty2;}
|
|
|
|
|
if(rot == 2 && flip == 1 ) { x=textx1;}
|
|
|
|
|
if(rot == 3 && flip == 1 ) { y=texty1;}
|
|
|
|
|
}
|
|
|
|
|
if(vcenter) {
|
|
|
|
|
if(rot == 0 && flip == 0 ) { y=texty1;}
|
|
|
|
|
if(rot == 1 && flip == 0 ) { x=textx2;}
|
|
|
|
|
if(rot == 2 && flip == 0 ) { y=texty2;}
|
|
|
|
|
if(rot == 3 && flip == 0 ) { x=textx1;}
|
|
|
|
|
if(rot == 0 && flip == 1 ) { y=texty1;}
|
|
|
|
|
if(rot == 1 && flip == 1 ) { x=textx2;}
|
|
|
|
|
if(rot == 2 && flip == 1 ) { y=texty2;}
|
|
|
|
|
if(rot == 3 && flip == 1 ) { x=textx1;}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-19 15:08:40 +01:00
|
|
|
set_cairo_color(layer);
|
2022-12-25 23:13:59 +01:00
|
|
|
cairo_set_font_size(xctx->cairo_ctx, size*xctx->mooz);
|
|
|
|
|
cairo_set_font_size(xctx->cairo_save_ctx, size*xctx->mooz);
|
2020-12-06 02:10:53 +01:00
|
|
|
cairo_font_extents(xctx->cairo_ctx, &fext);
|
2020-11-18 18:29:14 +01:00
|
|
|
dbg(1, "draw_string(): size * mooz=%g height=%g ascent=%g descent=%g\n",
|
|
|
|
|
size * xctx->mooz, fext.height, fext.ascent, fext.descent);
|
2020-08-08 15:47:34 +02:00
|
|
|
llength=0;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &sss, estr);
|
2020-08-31 02:25:41 +02:00
|
|
|
tt=ss=sss;
|
2020-08-08 15:47:34 +02:00
|
|
|
for(;;) {
|
|
|
|
|
c=*ss;
|
|
|
|
|
if(c=='\n' || c==0) {
|
|
|
|
|
*ss='\0';
|
2020-12-23 05:07:39 +01:00
|
|
|
/*fprintf(errfp, "cairo_draw_string(): tt=%s, longest line: %d\n", tt, longest_line); */
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) cairo_draw_string_line(xctx->cairo_ctx, tt, x, y, rot, flip,
|
2020-12-23 05:07:39 +01:00
|
|
|
lineno, fext.height, fext.ascent, fext.descent, llength, no_of_lines, longest_line);
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap) cairo_draw_string_line(xctx->cairo_save_ctx, tt, x, y, rot, flip,
|
2020-12-23 05:07:39 +01:00
|
|
|
lineno, fext.height, fext.ascent, fext.descent, llength, no_of_lines, longest_line);
|
2023-02-18 09:44:11 +01:00
|
|
|
++lineno;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(c==0) break;
|
|
|
|
|
*ss='\n';
|
|
|
|
|
tt=ss+1;
|
|
|
|
|
llength=0;
|
|
|
|
|
} else {
|
2023-02-18 09:44:11 +01:00
|
|
|
++llength;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2023-02-18 09:44:11 +01:00
|
|
|
++ss;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &sss);
|
|
|
|
|
my_free(_ALLOC_ID_, &estr);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else /* !HAS_CAIRO */
|
|
|
|
|
|
|
|
|
|
/* no CAIRO version */
|
2020-12-05 03:16:01 +01:00
|
|
|
void draw_string(int layer, int what, const char *str, short rot, short flip, int hcenter, int vcenter,
|
2020-10-12 13:13:31 +02:00
|
|
|
double x1,double y1, double xscale, double yscale)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2021-11-18 01:55:01 +01:00
|
|
|
double textx1,textx2,texty1,texty2;
|
2020-08-08 15:47:34 +02:00
|
|
|
double a=0.0,yy;
|
|
|
|
|
register double rx1=0,rx2=0,ry1=0,ry2=0;
|
|
|
|
|
double curr_x1,curr_y1,curr_x2,curr_y2;
|
2020-12-24 12:23:48 +01:00
|
|
|
double zx1, invxscale;
|
|
|
|
|
int pos=0,pos2=0;
|
|
|
|
|
unsigned int cc;
|
|
|
|
|
double *char_ptr_x1,*char_ptr_y1,*char_ptr_x2,*char_ptr_y2;
|
2022-04-28 00:00:51 +02:00
|
|
|
int i,lines, no_of_lines;
|
|
|
|
|
double longest_line;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
if(str==NULL || !has_x ) return;
|
|
|
|
|
dbg(2, "draw_string(): string=%s\n",str);
|
2020-10-15 17:39:21 +02:00
|
|
|
if(xscale*FONTWIDTH*xctx->mooz<1) {
|
|
|
|
|
dbg(1, "draw_string(): xscale=%.16g zoom=%.16g \n",xscale,xctx->zoom);
|
2020-08-08 15:47:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-05-23 00:57:45 +02:00
|
|
|
char *estr = my_expand(str, tclgetintvar("tabstop"));
|
|
|
|
|
text_bbox(estr, xscale, yscale, rot, flip, hcenter, vcenter, x1,y1,
|
2020-12-23 05:07:39 +01:00
|
|
|
&textx1,&texty1,&textx2,&texty2, &no_of_lines, &longest_line);
|
|
|
|
|
if(!textclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,
|
2024-09-06 18:33:09 +02:00
|
|
|
textx1,texty1,textx2,texty2)) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &estr);
|
2024-09-06 18:33:09 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2025-04-16 02:54:56 +02:00
|
|
|
xscale*=tclgetdoublevar("nocairo_font_xscale") * cairo_font_scale;
|
|
|
|
|
yscale*=tclgetdoublevar("nocairo_font_yscale") * cairo_font_scale;
|
2020-08-08 15:47:34 +02:00
|
|
|
x1=textx1;y1=texty1;
|
|
|
|
|
if(rot&1) {y1=texty2;rot=3;}
|
|
|
|
|
else rot=0;
|
|
|
|
|
flip = 0; yy=y1;
|
|
|
|
|
invxscale=1/xscale;
|
2024-05-23 00:57:45 +02:00
|
|
|
while(estr[pos2]) {
|
|
|
|
|
cc = (unsigned char)estr[pos2++];
|
2020-08-08 15:47:34 +02:00
|
|
|
if(cc>127) cc= '?';
|
|
|
|
|
if(cc=='\n') {
|
|
|
|
|
yy+=(FONTHEIGHT+FONTDESCENT+FONTWHITESPACE)* yscale;
|
|
|
|
|
pos=0;
|
|
|
|
|
a=0.0;
|
|
|
|
|
continue;
|
2020-10-12 13:13:31 +02:00
|
|
|
}
|
2022-04-28 10:12:16 +02:00
|
|
|
lines=(int)character[cc][0]*4;
|
2020-08-08 15:47:34 +02:00
|
|
|
char_ptr_x1=character[cc]+1;
|
|
|
|
|
char_ptr_y1=character[cc]+2;
|
|
|
|
|
char_ptr_x2=character[cc]+3;
|
|
|
|
|
char_ptr_y2=character[cc]+4;
|
|
|
|
|
zx1=a+x1*invxscale;
|
|
|
|
|
for(i=0;i<lines;i+=4) {
|
|
|
|
|
curr_x1 = ( char_ptr_x1[i]+ zx1 ) * xscale ;
|
|
|
|
|
curr_y1 = ( char_ptr_y1[i] ) * yscale+yy;
|
|
|
|
|
curr_x2 = ( char_ptr_x2[i]+ zx1 ) * xscale ;
|
|
|
|
|
curr_y2 = ( char_ptr_y2[i] ) * yscale+yy;
|
2020-11-03 12:10:55 +01:00
|
|
|
ROTATION(rot, flip, x1,y1,curr_x1,curr_y1,rx1,ry1);
|
|
|
|
|
ROTATION(rot, flip, x1,y1,curr_x2,curr_y2,rx2,ry2);
|
2020-08-08 15:47:34 +02:00
|
|
|
ORDER(rx1,ry1,rx2,ry2);
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(layer, what, rx1, ry1, rx2, ry2, 0.0, 0, NULL);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2023-02-18 09:44:11 +01:00
|
|
|
++pos;
|
2020-08-08 15:47:34 +02:00
|
|
|
a += FONTWIDTH+FONTWHITESPACE;
|
|
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &estr);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAS_CAIRO */
|
|
|
|
|
|
2020-12-05 03:16:01 +01:00
|
|
|
void draw_temp_string(GC gctext, int what, const char *str, short rot, short flip, int hcenter, int vcenter,
|
2020-10-12 13:13:31 +02:00
|
|
|
double x1,double y1, double xscale, double yscale)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2021-11-18 01:55:01 +01:00
|
|
|
double textx1,textx2,texty1,texty2;
|
2020-12-23 05:07:39 +01:00
|
|
|
int tmp;
|
2022-04-28 00:00:51 +02:00
|
|
|
double dtmp;
|
2024-05-23 00:57:45 +02:00
|
|
|
char *estr = NULL;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(!has_x) return;
|
2023-09-23 01:46:39 +02:00
|
|
|
|
2024-05-23 00:57:45 +02:00
|
|
|
estr = my_expand(str, tclgetintvar("tabstop"));
|
|
|
|
|
dbg(2, "draw_string(): string=%s\n",estr);
|
|
|
|
|
if(!text_bbox(estr, xscale, yscale, rot, flip, hcenter, vcenter, x1,y1,
|
2024-09-06 18:33:09 +02:00
|
|
|
&textx1,&texty1,&textx2,&texty2, &tmp, &dtmp)) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &estr);
|
2024-09-06 18:33:09 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
drawtemprect(gctext,what, textx1,texty1,textx2,texty2);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &estr);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-21 13:18:52 +02:00
|
|
|
void get_sym_text_layer(int inst, int text_n, int *layer)
|
|
|
|
|
{
|
|
|
|
|
char attr[50];
|
|
|
|
|
const char *tl=NULL;
|
|
|
|
|
int lay;
|
|
|
|
|
int sym_n = xctx->inst[inst].ptr;
|
|
|
|
|
|
|
|
|
|
*layer = -1;
|
|
|
|
|
if(sym_n >= 0 && xctx->sym[sym_n].texts > text_n) {
|
|
|
|
|
if(xctx->inst[inst].prop_ptr && strstr(xctx->inst[inst].prop_ptr, "text_layer_")) {
|
|
|
|
|
my_snprintf(attr, S(attr), "text_layer_%d", text_n);
|
|
|
|
|
tl = get_tok_value(xctx->inst[inst].prop_ptr, attr, 0);
|
|
|
|
|
} else {
|
|
|
|
|
xctx->tok_size = 0;
|
|
|
|
|
}
|
|
|
|
|
if(xctx->tok_size) {
|
|
|
|
|
lay = atoi(tl);
|
|
|
|
|
if(lay >= 0 && lay < cadlayers) *layer = lay;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-19 17:12:46 +01:00
|
|
|
|
2023-12-03 12:26:18 +01:00
|
|
|
void get_sym_text_size(int inst, int text_n, double *xscale, double *yscale)
|
|
|
|
|
{
|
|
|
|
|
char attr[50];
|
2023-12-18 23:36:01 +01:00
|
|
|
const char *ts=NULL;
|
2023-12-03 12:26:18 +01:00
|
|
|
double size;
|
|
|
|
|
int sym_n = xctx->inst[inst].ptr;
|
|
|
|
|
|
|
|
|
|
if(sym_n >= 0 && xctx->sym[sym_n].texts > text_n) {
|
2023-12-05 17:32:48 +01:00
|
|
|
if(xctx->inst[inst].prop_ptr && strstr(xctx->inst[inst].prop_ptr, "text_size_")) {
|
2023-12-03 23:11:36 +01:00
|
|
|
my_snprintf(attr, S(attr), "text_size_%d", text_n);
|
|
|
|
|
ts = get_tok_value(xctx->inst[inst].prop_ptr, attr, 0);
|
|
|
|
|
} else {
|
|
|
|
|
xctx->tok_size = 0;
|
|
|
|
|
}
|
2023-12-03 12:26:18 +01:00
|
|
|
if(xctx->tok_size) {
|
|
|
|
|
size = atof(ts);
|
|
|
|
|
*xscale = size;
|
|
|
|
|
*yscale = size;
|
|
|
|
|
} else {
|
2023-12-03 23:11:36 +01:00
|
|
|
xText *txtptr;
|
|
|
|
|
txtptr = &(xctx->sym[sym_n].text[text_n]);
|
2023-12-03 12:26:18 +01:00
|
|
|
*xscale = txtptr->xscale;
|
|
|
|
|
*yscale = txtptr->yscale;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
*xscale = *yscale = 0.0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-12-19 17:12:46 +01:00
|
|
|
/*
|
|
|
|
|
* layer: the set of symbol objects on xschem layer 'layer' to draw
|
|
|
|
|
* c : the layer 'c' to draw those objects on (if != layer it is the hilight color)
|
|
|
|
|
*/
|
2020-12-05 03:16:01 +01:00
|
|
|
void draw_symbol(int what,int c, int n,int layer,short tmp_flip, short rot,
|
2020-10-12 13:13:31 +02:00
|
|
|
double xoffset, double yoffset)
|
2020-08-08 15:47:34 +02:00
|
|
|
/* draws current layer only, should be called within */
|
2023-02-18 09:44:11 +01:00
|
|
|
{ /* a "for(i=0;i<cadlayers; ++i)" loop */
|
2023-05-10 03:13:13 +02:00
|
|
|
int k, j, textlayer, hide = 0, disabled = 0;
|
2020-12-19 17:12:46 +01:00
|
|
|
double x0,y0,x1,y1,x2,y2;
|
2022-01-18 03:37:54 +01:00
|
|
|
double *x, *y; /* polygon point arrays */
|
2020-12-05 03:16:01 +01:00
|
|
|
short flip;
|
2022-01-24 22:58:30 +01:00
|
|
|
xLine *line;
|
|
|
|
|
xRect *rect;
|
|
|
|
|
xArc *arc;
|
|
|
|
|
xPoly *polygon;
|
2020-10-12 13:13:31 +02:00
|
|
|
xText text;
|
|
|
|
|
register xSymbol *symptr;
|
2020-12-19 17:12:46 +01:00
|
|
|
char *type;
|
2023-06-06 15:22:45 +02:00
|
|
|
int lvs_ignore = 0;
|
2025-04-05 11:27:45 +02:00
|
|
|
int c_for_text;
|
2020-12-07 20:04:57 +01:00
|
|
|
#if HAS_CAIRO==1
|
2021-11-10 13:43:08 +01:00
|
|
|
const char *textfont;
|
2020-08-08 15:47:34 +02:00
|
|
|
#endif
|
2020-12-19 17:12:46 +01:00
|
|
|
|
2025-01-29 16:22:44 +01:00
|
|
|
type = xctx->sym[xctx->inst[n].ptr].type;
|
2023-06-06 15:22:45 +02:00
|
|
|
lvs_ignore=tclgetboolvar("lvs_ignore");
|
2025-01-29 16:22:44 +01:00
|
|
|
if(!has_x) return;
|
2020-10-15 17:39:21 +02:00
|
|
|
if(xctx->inst[n].ptr == -1) return;
|
2023-05-12 12:09:20 +02:00
|
|
|
if(layer == 0) {
|
2023-06-06 15:22:45 +02:00
|
|
|
xctx->inst[n].flags &= ~IGNORE_INST; /* clear bit */
|
2023-05-12 01:29:38 +02:00
|
|
|
if( type && strcmp(type, "launcher") && strcmp(type, "logo") &&
|
2023-05-10 17:46:16 +02:00
|
|
|
strcmp(type, "probe") &&
|
|
|
|
|
strcmp(type, "architecture") && strcmp(type, "noconn")) {
|
2025-01-29 16:22:44 +01:00
|
|
|
if(skip_instance(n, 1, lvs_ignore)) {
|
|
|
|
|
xctx->inst[n].flags |= IGNORE_INST;
|
2023-05-10 17:46:16 +02:00
|
|
|
}
|
2023-05-10 03:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-06-07 03:41:49 +02:00
|
|
|
if(shorted_instance(n, lvs_ignore)) {
|
2023-06-06 15:22:45 +02:00
|
|
|
c = PINLAYER;
|
|
|
|
|
what = NOW;
|
|
|
|
|
disabled = 2;
|
|
|
|
|
}
|
|
|
|
|
else if(xctx->inst[n].flags & IGNORE_INST) {
|
2023-05-10 17:46:16 +02:00
|
|
|
c = GRIDLAYER;
|
|
|
|
|
what = NOW;
|
|
|
|
|
disabled = 1;
|
2023-05-10 03:13:13 +02:00
|
|
|
}
|
2025-04-21 11:35:18 +02:00
|
|
|
if( (xctx->inst[n].flags & HIDE_INST) || ((xctx->inst[n].ptr + xctx->sym)->flags & HIDE_INST) ||
|
|
|
|
|
(xctx->hide_symbols==1 && (xctx->inst[n].ptr + xctx->sym)->type &&
|
2023-03-10 04:42:21 +01:00
|
|
|
!strcmp( (xctx->inst[n].ptr+ xctx->sym)->type, "subcircuit") ) ||
|
|
|
|
|
(xctx->hide_symbols == 2) ) {
|
2020-09-05 00:58:56 +02:00
|
|
|
hide = 1;
|
|
|
|
|
} else {
|
|
|
|
|
hide = 0;
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
if(layer==0) {
|
2020-10-15 17:39:21 +02:00
|
|
|
x1=X_TO_SCREEN(xctx->inst[n].x1+xoffset); /* 20150729 added xoffset, yoffset */
|
|
|
|
|
x2=X_TO_SCREEN(xctx->inst[n].x2+xoffset);
|
|
|
|
|
y1=Y_TO_SCREEN(xctx->inst[n].y1+yoffset);
|
|
|
|
|
y2=Y_TO_SCREEN(xctx->inst[n].y2+yoffset);
|
2023-10-30 18:03:52 +01:00
|
|
|
if(RECT_OUTSIDE(x1,y1,x2,y2,xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2))
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-15 17:39:21 +02:00
|
|
|
xctx->inst[n].flags|=1;
|
2020-08-08 15:47:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-12-19 17:12:46 +01:00
|
|
|
else if(
|
|
|
|
|
xctx->hilight_nets && /* if highlights... */
|
|
|
|
|
c == 0 && /* we are not drawing highlighted inst */
|
|
|
|
|
/* otherwise c > layer... */
|
|
|
|
|
type && /* ... and type... */
|
|
|
|
|
(
|
|
|
|
|
( /* ... and inst is hilighted ... */
|
|
|
|
|
IS_LABEL_SH_OR_PIN(type) && xctx->inst[n].node && xctx->inst[n].node[0] &&
|
2021-11-23 15:03:51 +01:00
|
|
|
bus_hilight_hash_lookup(xctx->inst[n].node[0], 0, XLOOKUP )
|
2022-10-12 11:56:02 +02:00
|
|
|
) || (/* !IS_LABEL_SH_OR_PIN(type) && */ (xctx->inst[n].color != -10000)) )) {
|
|
|
|
|
xctx->inst[n].flags|=1; /* ... then SKIP instance now and for following layers */
|
2020-12-19 17:12:46 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2021-11-17 23:12:17 +01:00
|
|
|
else if(!xctx->only_probes && (xctx->inst[n].x2 - xctx->inst[n].x1) * xctx->mooz < 3 &&
|
2020-12-19 17:12:46 +01:00
|
|
|
(xctx->inst[n].y2 - xctx->inst[n].y1) * xctx->mooz < 3) {
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(SYMLAYER, NOW, xctx->inst[n].xx1, xctx->inst[n].yy1, xctx->inst[n].xx2, xctx->inst[n].yy2,
|
|
|
|
|
0.0, 0, -1, -1);
|
2020-12-19 17:12:46 +01:00
|
|
|
xctx->inst[n].flags|=1;
|
2020-12-20 19:48:37 +01:00
|
|
|
return;
|
2020-12-19 17:12:46 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
xctx->inst[n].flags&=~1;
|
|
|
|
|
}
|
2023-05-12 10:31:09 +02:00
|
|
|
if(hide) {
|
2023-06-06 15:22:45 +02:00
|
|
|
int color = (disabled==1) ? GRIDLAYER : (disabled == 2) ? PINLAYER : SYMLAYER;
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(color, NOW, xctx->inst[n].xx1, xctx->inst[n].yy1, xctx->inst[n].xx2, xctx->inst[n].yy2,
|
|
|
|
|
0.0, 2, -1, -1);
|
2023-05-12 10:31:09 +02:00
|
|
|
}
|
2020-10-15 17:39:21 +02:00
|
|
|
} else if(xctx->inst[n].flags&1) {
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(2, "draw_symbol(): skipping inst %d\n", n);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-15 17:39:21 +02:00
|
|
|
flip = xctx->inst[n].flip;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(tmp_flip) flip = !flip;
|
2020-10-15 17:39:21 +02:00
|
|
|
rot = (xctx->inst[n].rot + rot ) & 0x3;
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
x0=xctx->inst[n].x0 + xoffset;
|
|
|
|
|
y0=xctx->inst[n].y0 + yoffset;
|
|
|
|
|
symptr = (xctx->inst[n].ptr+ xctx->sym);
|
2025-01-16 20:37:19 +01:00
|
|
|
|
2025-04-21 11:35:18 +02:00
|
|
|
if(layer == cadlayers) goto draw_texts;
|
|
|
|
|
if( (layer != PINLAYER && !xctx->enable_layer[layer]) ) return;
|
2025-01-16 20:37:19 +01:00
|
|
|
|
2020-09-05 00:58:56 +02:00
|
|
|
if(!hide) {
|
2023-02-18 09:44:11 +01:00
|
|
|
for(j=0;j< symptr->lines[layer]; ++j)
|
2020-09-05 00:58:56 +02:00
|
|
|
{
|
2023-05-12 10:31:09 +02:00
|
|
|
int dash;
|
2022-01-24 22:58:30 +01:00
|
|
|
line = &(symptr->line[layer])[j];
|
2023-06-06 15:22:45 +02:00
|
|
|
dash = (disabled == 1) ? 3 : line->dash;
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,line->x1,line->y1,x1,y1);
|
|
|
|
|
ROTATION(rot, flip, 0.0, 0.0,line->x2,line->y2,x2,y2);
|
2020-09-05 00:58:56 +02:00
|
|
|
ORDER(x1,y1,x2,y2);
|
2025-11-26 14:07:54 +01:00
|
|
|
if(line->bus == -1.0)
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(c,THICK, x0+x1, y0+y1, x0+x2, y0+y2, line->bus, dash, NULL);
|
2020-09-07 13:12:34 +02:00
|
|
|
else
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(c,what, x0+x1, y0+y1, x0+x2, y0+y2, line->bus, dash, NULL);
|
2020-09-05 00:58:56 +02:00
|
|
|
}
|
2023-02-18 09:44:11 +01:00
|
|
|
for(j=0;j< symptr->polygons[layer]; ++j)
|
2020-10-12 13:13:31 +02:00
|
|
|
{
|
2023-05-12 10:31:09 +02:00
|
|
|
int dash;
|
2024-02-27 10:24:21 +01:00
|
|
|
int bezier;
|
2022-01-24 22:58:30 +01:00
|
|
|
polygon = &(symptr->poly[layer])[j];
|
2024-02-27 10:24:21 +01:00
|
|
|
bezier = !strboolcmp(get_tok_value(polygon->prop_ptr, "bezier", 0), "true");
|
2023-06-06 15:22:45 +02:00
|
|
|
dash = (disabled == 1) ? 3 : polygon->dash;
|
2025-04-18 02:02:15 +02:00
|
|
|
x = my_malloc(_ALLOC_ID_, sizeof(double) * polygon->points);
|
|
|
|
|
y = my_malloc(_ALLOC_ID_, sizeof(double) * polygon->points);
|
2023-02-18 09:44:11 +01:00
|
|
|
for(k=0;k<polygon->points; ++k) {
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,polygon->x[k],polygon->y[k],x[k],y[k]);
|
2022-01-18 03:37:54 +01:00
|
|
|
x[k]+= x0;
|
|
|
|
|
y[k] += y0;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2025-11-27 22:03:10 +01:00
|
|
|
drawpolygon(c, NOW, x, y, polygon->points, polygon->fill, dash, polygon->bus, bezier); /* added fill */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &x);
|
|
|
|
|
my_free(_ALLOC_ID_, &y);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2023-02-18 09:44:11 +01:00
|
|
|
for(j=0;j< symptr->arcs[layer]; ++j)
|
2020-10-12 13:13:31 +02:00
|
|
|
{
|
2023-05-12 10:31:09 +02:00
|
|
|
int dash;
|
2025-01-29 16:22:44 +01:00
|
|
|
double angle;
|
2022-01-24 22:58:30 +01:00
|
|
|
arc = &(symptr->arc[layer])[j];
|
2023-06-06 15:22:45 +02:00
|
|
|
dash = (disabled == 1) ? 3 : arc->dash;
|
2020-09-05 00:58:56 +02:00
|
|
|
if(flip) {
|
2022-01-24 22:58:30 +01:00
|
|
|
angle = 270.*rot+180.-arc->b-arc->a;
|
2020-09-05 00:58:56 +02:00
|
|
|
} else {
|
2022-01-24 22:58:30 +01:00
|
|
|
angle = arc->a+rot*270.;
|
2020-09-05 00:58:56 +02:00
|
|
|
}
|
|
|
|
|
angle = fmod(angle, 360.);
|
|
|
|
|
if(angle<0.) angle+=360.;
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,arc->x,arc->y,x1,y1);
|
2025-11-28 13:31:45 +01:00
|
|
|
drawarc(c,what, x0+x1, y0+y1, arc->r, angle, arc->b, arc->fill, arc->bus, dash);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2020-09-05 01:43:27 +02:00
|
|
|
} /* if(!hide) */
|
|
|
|
|
|
2025-01-29 16:22:44 +01:00
|
|
|
if( (!hide && xctx->enable_layer[layer]) ||
|
2021-11-16 22:28:10 +01:00
|
|
|
(hide && layer == PINLAYER && xctx->enable_layer[layer]) ) {
|
2023-02-18 09:44:11 +01:00
|
|
|
for(j=0;j< symptr->rects[layer]; ++j)
|
2020-09-05 00:58:56 +02:00
|
|
|
{
|
2023-05-12 10:31:09 +02:00
|
|
|
int dash;
|
2022-01-24 22:58:30 +01:00
|
|
|
rect = &(symptr->rect[layer])[j];
|
2023-06-06 15:22:45 +02:00
|
|
|
dash = (disabled == 1) ? 3 : rect->dash;
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,rect->x1,rect->y1,x1,y1);
|
|
|
|
|
ROTATION(rot, flip, 0.0, 0.0,rect->x2,rect->y2,x2,y2);
|
2022-01-24 22:58:30 +01:00
|
|
|
#if HAS_CAIRO == 1
|
|
|
|
|
if(layer == GRIDLAYER && rect->flags & 1024) {
|
|
|
|
|
double xx1 = x0 + x1;
|
|
|
|
|
double yy1 = y0 + y1;
|
|
|
|
|
double xx2 = x0 + x2;
|
|
|
|
|
double yy2 = y0 + y2;
|
|
|
|
|
draw_image(1, rect, &xx1, &yy1, &xx2, &yy2, rot, flip);
|
|
|
|
|
} else
|
|
|
|
|
#endif
|
|
|
|
|
{
|
2024-04-06 23:13:14 +02:00
|
|
|
int ellipse_a = rect->ellipse_a;
|
|
|
|
|
int ellipse_b = rect->ellipse_b;
|
|
|
|
|
|
|
|
|
|
if(ellipse_a != -1 && ellipse_b != 360) {
|
|
|
|
|
if(flip) {
|
|
|
|
|
ellipse_a = 180 - ellipse_a - ellipse_b;
|
|
|
|
|
}
|
|
|
|
|
if(rot) {
|
|
|
|
|
if(rot == 3) {
|
|
|
|
|
ellipse_a += 90;
|
|
|
|
|
} else if(rot == 2) {
|
|
|
|
|
ellipse_a += 180;
|
|
|
|
|
} else if(rot == 1) {
|
|
|
|
|
ellipse_a += 270;
|
|
|
|
|
}
|
|
|
|
|
ellipse_a %= 360;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-24 22:58:30 +01:00
|
|
|
RECTORDER(x1,y1,x2,y2);
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(c,what, x0+x1, y0+y1, x0+x2, y0+y2, rect->bus, dash, ellipse_a, ellipse_b);
|
2024-04-05 01:34:54 +02:00
|
|
|
if(rect->fill) filledrect(c,what, x0+x1, y0+y1, x0+x2, y0+y2, rect->fill,
|
2024-04-06 23:13:14 +02:00
|
|
|
ellipse_a, ellipse_b);
|
2022-01-24 22:58:30 +01:00
|
|
|
}
|
2020-09-05 00:58:56 +02:00
|
|
|
}
|
2025-01-29 16:22:44 +01:00
|
|
|
} /* if( (!hide && xctx->enable_layer[layer]) || ... */
|
2025-01-16 20:37:19 +01:00
|
|
|
|
|
|
|
|
draw_texts:
|
|
|
|
|
|
2025-04-21 11:35:18 +02:00
|
|
|
if(xctx->sym_txt && !(xctx->inst[n].flags & HIDE_SYMBOL_TEXTS) && (layer == cadlayers)) {
|
2025-04-21 00:36:07 +02:00
|
|
|
if(c != layer) c_for_text = c;
|
|
|
|
|
else if(xctx->inst[n].flags & PIN_OR_LABEL) c_for_text = TEXTWIRELAYER;
|
2025-04-16 15:01:51 +02:00
|
|
|
else c_for_text = TEXTLAYER;
|
2023-02-18 09:44:11 +01:00
|
|
|
for(j=0;j< symptr->texts; ++j)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2023-12-03 12:26:18 +01:00
|
|
|
double xscale, yscale;
|
|
|
|
|
get_sym_text_size(n, j, &xscale, &yscale);
|
2020-10-12 13:13:31 +02:00
|
|
|
text = symptr->text[j];
|
2023-12-03 12:26:18 +01:00
|
|
|
if(!text.txt_ptr || !text.txt_ptr[0] || xscale*FONTWIDTH*xctx->mooz<1) continue;
|
2024-01-08 15:47:01 +01:00
|
|
|
if(!xctx->show_hidden_texts && (text.flags & (HIDE_TEXT | HIDE_TEXT_INSTANTIATED))) continue;
|
2020-09-05 00:58:56 +02:00
|
|
|
if( hide && text.txt_ptr && strcmp(text.txt_ptr, "@symname") && strcmp(text.txt_ptr, "@name") ) continue;
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,text.x0,text.y0,x1,y1);
|
2025-04-05 11:27:45 +02:00
|
|
|
textlayer = c_for_text;
|
2022-11-07 13:34:48 +01:00
|
|
|
/* do not allow custom text color on hilighted instances */
|
2024-10-07 15:50:29 +02:00
|
|
|
if(disabled == 1) textlayer = GRIDLAYER;
|
2023-06-06 15:22:45 +02:00
|
|
|
else if(disabled == 2) textlayer = PINLAYER;
|
2023-05-10 03:13:13 +02:00
|
|
|
else if( xctx->inst[n].color == -10000) {
|
2024-06-21 13:18:52 +02:00
|
|
|
int lay;
|
2024-10-07 15:50:29 +02:00
|
|
|
if(xctx->only_probes) textlayer = GRIDLAYER;
|
|
|
|
|
else {
|
|
|
|
|
get_sym_text_layer(n, j, &lay);
|
|
|
|
|
if(lay != -1) textlayer = lay;
|
|
|
|
|
else textlayer = symptr->text[j].layer;
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2025-04-05 11:27:45 +02:00
|
|
|
if(textlayer < 0 || textlayer >= cadlayers) textlayer = c_for_text;
|
2025-04-06 12:58:13 +02:00
|
|
|
if(xctx->draw_single_layer != -1 && textlayer != xctx->draw_single_layer) continue;
|
2020-12-22 18:31:08 +01:00
|
|
|
/* display PINLAYER colored instance texts even if PINLAYER disabled */
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->inst[n].color == -PINLAYER || xctx->enable_layer[textlayer]) {
|
2023-05-01 14:37:10 +02:00
|
|
|
char *txtptr = NULL;
|
2020-12-07 20:04:57 +01:00
|
|
|
#if HAS_CAIRO==1
|
2020-10-12 13:13:31 +02:00
|
|
|
textfont = symptr->text[j].font;
|
2022-01-13 12:46:55 +01:00
|
|
|
if((textfont && textfont[0]) || (symptr->text[j].flags & (TEXT_BOLD | TEXT_OBLIQUE | TEXT_ITALIC))) {
|
2020-09-23 18:15:26 +02:00
|
|
|
cairo_font_slant_t slant;
|
|
|
|
|
cairo_font_weight_t weight;
|
2021-11-10 13:43:08 +01:00
|
|
|
textfont = (symptr->text[j].font && symptr->text[j].font[0]) ?
|
|
|
|
|
symptr->text[j].font : tclgetvar("cairo_font_name");
|
2020-10-12 13:13:31 +02:00
|
|
|
weight = ( symptr->text[j].flags & TEXT_BOLD) ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL;
|
2020-09-23 18:15:26 +02:00
|
|
|
slant = CAIRO_FONT_SLANT_NORMAL;
|
2020-10-12 13:13:31 +02:00
|
|
|
if(symptr->text[j].flags & TEXT_ITALIC) slant = CAIRO_FONT_SLANT_ITALIC;
|
|
|
|
|
if(symptr->text[j].flags & TEXT_OBLIQUE) slant = CAIRO_FONT_SLANT_OBLIQUE;
|
2020-12-06 02:10:53 +01:00
|
|
|
cairo_save(xctx->cairo_ctx);
|
2020-12-03 18:21:23 +01:00
|
|
|
cairo_save(xctx->cairo_save_ctx);
|
2022-12-25 10:42:07 +01:00
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create(textfont, slant, weight);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font);
|
2022-12-25 23:13:59 +01:00
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
2024-08-29 08:13:06 +02:00
|
|
|
dbg(1, "draw_symbol(): drawing string: before translate(): text.txt_ptr=%s\n", text.txt_ptr);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &txtptr, translate(n, text.txt_ptr));
|
2024-03-21 00:12:48 +01:00
|
|
|
/* do another round of substitutions if some @var are found, but if not found leave @var as is */
|
2025-01-04 11:33:58 +01:00
|
|
|
dbg(1, "draw_symbol(): drawing string: str=%s prop=%s\n",
|
|
|
|
|
txtptr, text.prop_ptr ? text.prop_ptr : "<NULL>");
|
2025-08-22 00:08:22 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &txtptr, translate3(txtptr, 0, xctx->inst[n].prop_ptr,
|
2025-02-07 14:39:21 +01:00
|
|
|
xctx->sym[xctx->inst[n].ptr].templ, NULL, NULL));
|
2024-08-29 08:13:06 +02:00
|
|
|
dbg(1, "draw_symbol(): after translate3: str=%s\n", txtptr);
|
2020-08-31 02:25:41 +02:00
|
|
|
draw_string(textlayer, what, txtptr,
|
2020-08-08 15:47:34 +02:00
|
|
|
(text.rot + ( (flip && (text.rot & 1) ) ? rot+2 : rot) ) & 0x3,
|
2020-10-12 13:13:31 +02:00
|
|
|
flip^text.flip, text.hcenter, text.vcenter,
|
2023-12-03 12:26:18 +01:00
|
|
|
x0+x1, y0+y1, xscale, yscale);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &txtptr);
|
2020-12-07 20:12:52 +01:00
|
|
|
#if HAS_CAIRO!=1
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(textlayer, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, -1, -1);
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(textlayer, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, NULL);
|
2020-08-29 11:58:50 +02:00
|
|
|
#endif
|
2020-12-07 20:04:57 +01:00
|
|
|
#if HAS_CAIRO==1
|
2022-01-13 12:46:55 +01:00
|
|
|
if( (textfont && textfont[0]) || (symptr->text[j].flags & (TEXT_BOLD | TEXT_OBLIQUE | TEXT_ITALIC))) {
|
2020-12-06 02:10:53 +01:00
|
|
|
cairo_restore(xctx->cairo_ctx);
|
2020-12-03 18:21:23 +01:00
|
|
|
cairo_restore(xctx->cairo_save_ctx);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-05 03:16:01 +01:00
|
|
|
void draw_temp_symbol(int what, GC gc, int n,int layer,short tmp_flip, short rot,
|
2020-08-08 15:47:34 +02:00
|
|
|
double xoffset, double yoffset)
|
|
|
|
|
/* draws current layer only, should be called within */
|
2023-02-18 09:44:11 +01:00
|
|
|
{ /* a "for(i=0;i<cadlayers; ++i)" loop */
|
2023-03-10 04:42:21 +01:00
|
|
|
int j, hide = 0;
|
2020-08-08 15:47:34 +02:00
|
|
|
double x0,y0,x1,y1,x2,y2;
|
2020-12-05 03:16:01 +01:00
|
|
|
short flip;
|
2022-01-24 22:58:30 +01:00
|
|
|
xLine *line;
|
|
|
|
|
xPoly *polygon;
|
|
|
|
|
xRect *rect;
|
|
|
|
|
xArc *arc;
|
2020-10-12 13:13:31 +02:00
|
|
|
xText text;
|
|
|
|
|
register xSymbol *symptr;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2020-12-07 20:04:57 +01:00
|
|
|
#if HAS_CAIRO==1
|
2020-08-08 15:47:34 +02:00
|
|
|
int customfont;
|
|
|
|
|
#endif
|
2024-03-02 03:31:24 +01:00
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
if(xctx->inst[n].ptr == -1) return;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(!has_x) return;
|
|
|
|
|
|
2025-04-21 11:35:18 +02:00
|
|
|
if( (xctx->inst[n].flags & HIDE_INST) || ((xctx->inst[n].ptr + xctx->sym)->flags & HIDE_INST) ||
|
2023-03-10 04:42:21 +01:00
|
|
|
(xctx->hide_symbols==1 && (xctx->inst[n].ptr+ xctx->sym)->prop_ptr &&
|
|
|
|
|
!strcmp( (xctx->inst[n].ptr+ xctx->sym)->type, "subcircuit") ) ||
|
|
|
|
|
(xctx->hide_symbols == 2) ) {
|
|
|
|
|
hide = 1;
|
|
|
|
|
} else {
|
|
|
|
|
hide = 0;
|
|
|
|
|
}
|
2023-10-30 17:17:00 +01:00
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
flip = xctx->inst[n].flip;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(tmp_flip) flip = !flip;
|
2020-10-15 17:39:21 +02:00
|
|
|
rot = (xctx->inst[n].rot + rot ) & 0x3;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
if(layer==0) {
|
2020-10-15 17:39:21 +02:00
|
|
|
x1=X_TO_SCREEN(xctx->inst[n].x1+xoffset); /* 20150729 added xoffset, yoffset */
|
|
|
|
|
x2=X_TO_SCREEN(xctx->inst[n].x2+xoffset);
|
|
|
|
|
y1=Y_TO_SCREEN(xctx->inst[n].y1+yoffset);
|
|
|
|
|
y2=Y_TO_SCREEN (xctx->inst[n].y2+yoffset);
|
2023-10-30 18:03:52 +01:00
|
|
|
if(RECT_OUTSIDE(x1,y1,x2,y2,xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2))
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-15 17:39:21 +02:00
|
|
|
xctx->inst[n].flags|=1;
|
2020-08-08 15:47:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2021-11-17 23:12:17 +01:00
|
|
|
else if(!xctx->only_probes && (xctx->inst[n].x2 - xctx->inst[n].x1) * xctx->mooz < 3 &&
|
2020-12-20 19:48:37 +01:00
|
|
|
(xctx->inst[n].y2 - xctx->inst[n].y1) * xctx->mooz < 3) {
|
|
|
|
|
drawtemprect(gc, what, xctx->inst[n].xx1 + xoffset, xctx->inst[n].yy1 + yoffset,
|
|
|
|
|
xctx->inst[n].xx2 + xoffset, xctx->inst[n].yy2 + yoffset);
|
|
|
|
|
xctx->inst[n].flags|=1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-15 17:39:21 +02:00
|
|
|
else xctx->inst[n].flags&=~1;
|
2023-03-10 04:42:21 +01:00
|
|
|
if(hide) {
|
2023-10-30 17:17:00 +01:00
|
|
|
/*
|
|
|
|
|
* symptr = (xctx->inst[n].ptr+ xctx->sym);
|
|
|
|
|
* x0=xctx->inst[n].x0;
|
|
|
|
|
* y0=xctx->inst[n].y0;
|
|
|
|
|
* x0 += xoffset;
|
|
|
|
|
* y0 += yoffset;
|
2024-11-25 14:29:19 +01:00
|
|
|
* ROTATION(rot, flip, 0.0, 0.0,symptr->minx, symptr->miny,x1,y1);
|
|
|
|
|
* ROTATION(rot, flip, 0.0, 0.0,symptr->maxx, symptr->maxy,x2,y2);
|
2023-10-30 17:17:00 +01:00
|
|
|
* RECTORDER(x1,y1,x2,y2);
|
|
|
|
|
* drawtemprect(gc,what, x0+x1, y0+y1, x0+x2, y0+y2);
|
|
|
|
|
*/
|
|
|
|
|
drawtemprect(gc,what,xctx->inst[n].xx1 + xoffset, xctx->inst[n].yy1 + yoffset,
|
|
|
|
|
xctx->inst[n].xx2 + xoffset, xctx->inst[n].yy2 + yoffset);
|
|
|
|
|
}
|
2020-10-15 17:39:21 +02:00
|
|
|
} else if(xctx->inst[n].flags&1) {
|
2020-08-08 15:47:34 +02:00
|
|
|
dbg(2, "draw_symbol(): skipping inst %d\n", n);
|
|
|
|
|
return;
|
|
|
|
|
} /* /20150424 */
|
|
|
|
|
|
2020-10-15 17:39:21 +02:00
|
|
|
x0=xctx->inst[n].x0 + xoffset;
|
|
|
|
|
y0=xctx->inst[n].y0 + yoffset;
|
|
|
|
|
symptr = (xctx->inst[n].ptr+ xctx->sym);
|
2023-03-10 04:42:21 +01:00
|
|
|
if(!hide) {
|
|
|
|
|
for(j=0;j< symptr->lines[layer]; ++j)
|
|
|
|
|
{
|
|
|
|
|
line = &(symptr->line[layer])[j];
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,line->x1,line->y1,x1,y1);
|
|
|
|
|
ROTATION(rot, flip, 0.0, 0.0,line->x2,line->y2,x2,y2);
|
2023-03-10 04:42:21 +01:00
|
|
|
ORDER(x1,y1,x2,y2);
|
2025-11-26 14:07:54 +01:00
|
|
|
if(line->bus == -1.0)
|
2023-03-10 04:42:21 +01:00
|
|
|
drawtempline(gc,THICK, x0+x1, y0+y1, x0+x2, y0+y2);
|
|
|
|
|
else
|
|
|
|
|
drawtempline(gc,what, x0+x1, y0+y1, x0+x2, y0+y2);
|
|
|
|
|
}
|
|
|
|
|
for(j=0;j< symptr->polygons[layer]; ++j)
|
|
|
|
|
{
|
2024-02-27 10:24:21 +01:00
|
|
|
int bezier;
|
2023-03-10 04:42:21 +01:00
|
|
|
polygon = &(symptr->poly[layer])[j];
|
2024-02-27 10:24:21 +01:00
|
|
|
bezier = !strboolcmp(get_tok_value(polygon->prop_ptr, "bezier", 0), "true");
|
2023-03-10 04:42:21 +01:00
|
|
|
{ /* scope block so we declare some auxiliary arrays for coord transforms. 20171115 */
|
|
|
|
|
int k;
|
2025-04-18 02:02:15 +02:00
|
|
|
double *x = my_malloc(_ALLOC_ID_, sizeof(double) * polygon->points);
|
|
|
|
|
double *y = my_malloc(_ALLOC_ID_, sizeof(double) * polygon->points);
|
2023-03-10 04:42:21 +01:00
|
|
|
for(k=0;k<polygon->points; ++k) {
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,polygon->x[k],polygon->y[k],x[k],y[k]);
|
2023-03-10 04:42:21 +01:00
|
|
|
x[k] += x0;
|
|
|
|
|
y[k] += y0;
|
|
|
|
|
}
|
2024-02-27 10:24:21 +01:00
|
|
|
drawtemppolygon(gc, NOW, x, y, polygon->points, bezier);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &x);
|
|
|
|
|
my_free(_ALLOC_ID_, &y);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-10 04:42:21 +01:00
|
|
|
|
|
|
|
|
for(j=0;j< symptr->rects[layer]; ++j)
|
|
|
|
|
{
|
|
|
|
|
rect = &(symptr->rect[layer])[j];
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,rect->x1,rect->y1,x1,y1);
|
|
|
|
|
ROTATION(rot, flip, 0.0, 0.0,rect->x2,rect->y2,x2,y2);
|
2023-03-10 04:42:21 +01:00
|
|
|
RECTORDER(x1,y1,x2,y2);
|
|
|
|
|
drawtemprect(gc,what, x0+x1, y0+y1, x0+x2, y0+y2);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2023-03-10 04:42:21 +01:00
|
|
|
for(j=0;j< symptr->arcs[layer]; ++j)
|
|
|
|
|
{
|
2025-01-29 16:22:44 +01:00
|
|
|
double angle;
|
2023-03-10 04:42:21 +01:00
|
|
|
arc = &(symptr->arc[layer])[j];
|
|
|
|
|
if(flip) {
|
|
|
|
|
angle = 270.*rot+180.-arc->b-arc->a;
|
|
|
|
|
} else {
|
|
|
|
|
angle = arc->a+rot*270.;
|
|
|
|
|
}
|
|
|
|
|
angle = fmod(angle, 360.);
|
|
|
|
|
if(angle<0.) angle+=360.;
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,arc->x,arc->y,x1,y1);
|
2023-03-10 04:42:21 +01:00
|
|
|
drawtemparc(gc, what, x0+x1, y0+y1, arc->r, angle, arc->b);
|
|
|
|
|
}
|
2023-05-13 13:13:35 +02:00
|
|
|
|
|
|
|
|
if( !(xctx->inst[n].flags & HIDE_SYMBOL_TEXTS) && layer==SELLAYER && xctx->sym_txt)
|
2023-03-10 04:42:21 +01:00
|
|
|
{
|
2023-05-01 14:37:10 +02:00
|
|
|
char *txtptr = NULL;
|
2023-03-10 04:42:21 +01:00
|
|
|
for(j=0;j< symptr->texts; ++j)
|
|
|
|
|
{
|
2023-12-03 12:26:18 +01:00
|
|
|
double xscale, yscale;
|
|
|
|
|
|
|
|
|
|
get_sym_text_size(n, j, &xscale, &yscale);
|
2023-03-10 04:42:21 +01:00
|
|
|
text = symptr->text[j];
|
2023-12-03 12:26:18 +01:00
|
|
|
if(!text.txt_ptr || !text.txt_ptr[0] || xscale*FONTWIDTH*xctx->mooz<1) continue;
|
2024-01-08 15:47:01 +01:00
|
|
|
if(!xctx->show_hidden_texts && (text.flags & (HIDE_TEXT | HIDE_TEXT_INSTANTIATED))) continue;
|
2024-11-25 14:29:19 +01:00
|
|
|
ROTATION(rot, flip, 0.0, 0.0,text.x0,text.y0,x1,y1);
|
2023-03-10 04:42:21 +01:00
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
customfont = set_text_custom_font(&text);
|
|
|
|
|
#endif
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &txtptr, translate(n, text.txt_ptr));
|
2024-03-21 00:12:48 +01:00
|
|
|
/* do another round of substitutions if some @var are found, but if not found leave @var as is */
|
2025-08-22 00:08:22 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &txtptr, translate3(txtptr, 0, xctx->inst[n].prop_ptr,
|
2025-02-07 14:39:21 +01:00
|
|
|
xctx->sym[xctx->inst[n].ptr].templ, NULL, NULL));
|
2024-08-29 08:13:06 +02:00
|
|
|
dbg(1, "draw_temp_symbol(): after translate3: str=%s\n", txtptr);
|
2023-03-10 04:42:21 +01:00
|
|
|
if(txtptr[0]) draw_temp_string(gc, what, txtptr,
|
|
|
|
|
(text.rot + ( (flip && (text.rot & 1) ) ? rot+2 : rot) ) & 0x3,
|
2023-12-03 12:26:18 +01:00
|
|
|
flip^text.flip, text.hcenter, text.vcenter, x0+x1, y0+y1, xscale, yscale);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &txtptr);
|
2023-03-10 04:42:21 +01:00
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
if(customfont) {
|
|
|
|
|
cairo_restore(xctx->cairo_ctx);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
2022-12-25 23:13:59 +01:00
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-19 14:31:55 +01:00
|
|
|
static void drawgrid()
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2025-01-21 12:34:14 +01:00
|
|
|
double x, y, xax, yax, xx, yy;
|
2020-12-12 22:48:25 +01:00
|
|
|
double delta,tmp;
|
2024-11-16 10:19:34 +01:00
|
|
|
double mult;
|
2022-11-30 00:59:45 +01:00
|
|
|
#if DRAW_ALL_CAIRO==0
|
2020-12-12 22:48:25 +01:00
|
|
|
int i=0;
|
2025-03-07 14:45:13 +01:00
|
|
|
const char *psize_ptr;
|
2023-10-11 14:05:27 +02:00
|
|
|
int big_gr = tclgetboolvar("big_grid_points");
|
2025-03-07 14:45:13 +01:00
|
|
|
int grid_point_size = -1;
|
2025-01-21 12:34:14 +01:00
|
|
|
char dash_arr[2];
|
2023-10-11 14:05:27 +02:00
|
|
|
int axes = tclgetboolvar("draw_grid_axes");
|
2025-01-21 12:34:14 +01:00
|
|
|
|
2025-03-07 14:45:13 +01:00
|
|
|
psize_ptr = tclgetvar("grid_point_size");
|
|
|
|
|
if(psize_ptr[0]) grid_point_size = atoi(psize_ptr);
|
2025-01-21 12:34:14 +01:00
|
|
|
if(axes) {
|
|
|
|
|
dash_arr[0] = dash_arr[1] = (char) 3;
|
|
|
|
|
XSetDashes(display, xctx->gc[GRIDLAYER], 0, dash_arr, 1);
|
|
|
|
|
if(!big_gr) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[GRIDLAYER],
|
|
|
|
|
0, xDashType, xCap, xJoin);
|
|
|
|
|
} else {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[GRIDLAYER],
|
|
|
|
|
XLINEWIDTH(xctx->lw), xDashType, xCap, xJoin);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-30 00:59:45 +01:00
|
|
|
#endif
|
|
|
|
|
dbg(1, "drawgrid(): draw grid\n");
|
2021-11-10 13:43:08 +01:00
|
|
|
if( !tclgetboolvar("draw_grid") || !has_x) return;
|
|
|
|
|
delta=tclgetdoublevar("cadgrid")*xctx->mooz;
|
2022-11-30 00:59:45 +01:00
|
|
|
#if DRAW_ALL_CAIRO==1
|
|
|
|
|
set_cairo_color(GRIDLAYER);
|
|
|
|
|
#endif
|
2024-03-10 12:11:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if(delta < CADGRIDTHRESHOLD) {
|
|
|
|
|
mult = ceil( (log(CADGRIDTHRESHOLD) - log(delta) ) / log(CADGRIDMULTIPLY) );
|
|
|
|
|
delta = delta * pow(CADGRIDMULTIPLY, mult);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-03-07 14:45:13 +01:00
|
|
|
/* ************************ Draw axes ****************** */
|
2023-01-11 12:02:21 +01:00
|
|
|
#if DRAW_ALL_CAIRO==1
|
2025-01-21 12:34:14 +01:00
|
|
|
xax =floor(xctx->xorigin*xctx->mooz) + 0.5; yax = floor(xctx->yorigin*xctx->mooz) + 0.5;
|
2023-01-11 12:02:21 +01:00
|
|
|
#else
|
2025-01-21 12:34:14 +01:00
|
|
|
xax =xctx->xorigin*xctx->mooz; yax = xctx->yorigin*xctx->mooz;
|
2023-01-11 12:02:21 +01:00
|
|
|
#endif
|
2025-01-21 12:34:14 +01:00
|
|
|
if(yax > xctx->areay1 && yax < xctx->areay2) {
|
2022-11-30 00:59:45 +01:00
|
|
|
if(xctx->draw_window) {
|
|
|
|
|
#if DRAW_ALL_CAIRO==1
|
2025-01-21 12:34:14 +01:00
|
|
|
cairo_move_to(xctx->cairo_ctx, xctx->areax1+1, yax);
|
|
|
|
|
cairo_line_to(xctx->cairo_ctx, xctx->areax2-1, yax);
|
2022-11-30 00:59:45 +01:00
|
|
|
#else
|
2025-01-21 12:34:14 +01:00
|
|
|
if(axes) XDrawLine(display, xctx->window, xctx->gc[GRIDLAYER], xctx->areax1+1,
|
|
|
|
|
(int)yax, xctx->areax2-1, (int)yax);
|
2022-11-30 00:59:45 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if(xctx->draw_pixmap) {
|
|
|
|
|
#if DRAW_ALL_CAIRO==1
|
2025-01-21 12:34:14 +01:00
|
|
|
cairo_move_to(xctx->cairo_save_ctx, xctx->areax1+1, yax);
|
|
|
|
|
cairo_line_to(xctx->cairo_save_ctx, xctx->areax2-1, yax);
|
2022-11-30 00:59:45 +01:00
|
|
|
#else
|
2025-01-21 12:34:14 +01:00
|
|
|
if(axes) XDrawLine(display, xctx->save_pixmap, xctx->gc[GRIDLAYER], xctx->areax1+1, (int)yax,
|
|
|
|
|
xctx->areax2-1, (int)yax);
|
2022-11-30 00:59:45 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
2020-12-12 22:48:25 +01:00
|
|
|
}
|
2025-01-21 12:34:14 +01:00
|
|
|
if(xax > xctx->areax1 && xax < xctx->areax2) {
|
2022-11-30 00:59:45 +01:00
|
|
|
if(xctx->draw_window) {
|
|
|
|
|
#if DRAW_ALL_CAIRO==1
|
2025-01-21 12:34:14 +01:00
|
|
|
cairo_move_to(xctx->cairo_ctx, xax, xctx->areay1+1);
|
|
|
|
|
cairo_line_to(xctx->cairo_ctx, xax, xctx->areay2-1);
|
2022-11-30 00:59:45 +01:00
|
|
|
#else
|
2025-01-21 12:34:14 +01:00
|
|
|
if(axes) XDrawLine(display, xctx->window, xctx->gc[GRIDLAYER], (int)xax, xctx->areay1+1,
|
|
|
|
|
(int)xax, xctx->areay2-1);
|
2022-11-30 00:59:45 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if(xctx->draw_pixmap) {
|
|
|
|
|
#if DRAW_ALL_CAIRO==1
|
2025-01-21 12:34:14 +01:00
|
|
|
cairo_move_to(xctx->cairo_save_ctx, xax, xctx->areay1+1);
|
|
|
|
|
cairo_line_to(xctx->cairo_save_ctx, xax, xctx->areay2-1);
|
2022-11-30 00:59:45 +01:00
|
|
|
#else
|
2025-01-21 12:34:14 +01:00
|
|
|
if(axes) XDrawLine(display, xctx->save_pixmap, xctx->gc[GRIDLAYER], (int)xax, xctx->areay1+1,
|
|
|
|
|
(int)xax, xctx->areay2-1);
|
2022-11-30 00:59:45 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
2020-12-12 22:48:25 +01:00
|
|
|
}
|
2025-03-07 14:45:13 +01:00
|
|
|
/* ************************ /Draw axes ****************** */
|
|
|
|
|
|
2025-01-21 12:34:14 +01:00
|
|
|
#if DRAW_ALL_CAIRO==0
|
2025-03-07 14:45:13 +01:00
|
|
|
if(grid_point_size != -1) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[GRIDLAYER],
|
2025-03-07 15:59:43 +01:00
|
|
|
grid_point_size, LineSolid, CapProjecting, LINEJOIN);
|
2025-03-07 14:45:13 +01:00
|
|
|
} else if(!big_gr) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[GRIDLAYER],
|
|
|
|
|
0, LineSolid, LINECAP, LINEJOIN);
|
|
|
|
|
} else {
|
2025-01-21 12:34:14 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[GRIDLAYER],
|
|
|
|
|
XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-03-07 14:45:13 +01:00
|
|
|
if(grid_point_size >= 0) big_gr = 1;
|
|
|
|
|
|
2025-01-21 12:34:14 +01:00
|
|
|
tmp = floor((xctx->areay1+1)/delta)*delta-fmod(-xctx->yorigin*xctx->mooz, delta);
|
|
|
|
|
for(x=floor((xctx->areax1+1)/delta)*delta-fmod(-xctx->xorigin*xctx->mooz, delta); x < xctx->areax2; x += delta) {
|
|
|
|
|
xx = x;
|
2023-01-11 12:02:21 +01:00
|
|
|
#if DRAW_ALL_CAIRO==1
|
2025-01-21 12:34:14 +01:00
|
|
|
xx = floor(x) + 0.5;
|
2023-01-11 12:02:21 +01:00
|
|
|
#endif
|
2025-03-07 14:45:13 +01:00
|
|
|
if(axes && (int)xx == (int)xax) continue;
|
2020-12-12 22:48:25 +01:00
|
|
|
for(y=tmp; y < xctx->areay2; y += delta) {
|
2025-01-21 12:34:14 +01:00
|
|
|
yy = y;
|
2022-11-30 00:59:45 +01:00
|
|
|
#if DRAW_ALL_CAIRO==1
|
2025-01-21 12:34:14 +01:00
|
|
|
yy = floor(y) + 0.5;
|
|
|
|
|
#endif
|
2025-03-07 14:45:13 +01:00
|
|
|
if(axes && (int)yy == (int)yax) continue;
|
2025-01-21 12:34:14 +01:00
|
|
|
#if DRAW_ALL_CAIRO==1
|
|
|
|
|
if(xctx->draw_window) {
|
|
|
|
|
cairo_move_to(xctx->cairo_ctx, xx, yy) ;
|
|
|
|
|
cairo_close_path(xctx->cairo_ctx);
|
|
|
|
|
}
|
|
|
|
|
if(xctx->draw_pixmap) {
|
|
|
|
|
cairo_move_to(xctx->cairo_save_ctx, xx, yy);
|
|
|
|
|
cairo_close_path(xctx->cairo_save_ctx);
|
|
|
|
|
}
|
2022-11-30 00:59:45 +01:00
|
|
|
#else
|
2020-12-12 22:48:25 +01:00
|
|
|
if(i>=CADMAXGRIDPOINTS) {
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) {
|
2021-11-10 13:43:08 +01:00
|
|
|
if(big_gr) {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawSegments(display, xctx->window, xctx->gc[GRIDLAYER], xctx->biggridpoint, i);
|
2020-12-12 22:48:25 +01:00
|
|
|
} else {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawPoints(display, xctx->window, xctx->gc[GRIDLAYER], xctx->gridpoint, i, CoordModeOrigin);
|
2020-12-12 22:48:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap) {
|
2021-11-10 13:43:08 +01:00
|
|
|
if(big_gr) {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawSegments(display, xctx->save_pixmap, xctx->gc[GRIDLAYER], xctx->biggridpoint, i);
|
2020-12-12 22:48:25 +01:00
|
|
|
} else {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawPoints(display, xctx->save_pixmap, xctx->gc[GRIDLAYER], xctx->gridpoint, i, CoordModeOrigin);
|
2020-12-12 22:48:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i=0;
|
2020-12-12 13:36:39 +01:00
|
|
|
}
|
2021-11-10 13:43:08 +01:00
|
|
|
if(big_gr) {
|
2020-12-12 22:48:25 +01:00
|
|
|
xctx->biggridpoint[i].x1 = xctx->biggridpoint[i].x2 = (short)(x);
|
|
|
|
|
xctx->biggridpoint[i].y1 = xctx->biggridpoint[i].y2 = (short)(y);
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-12-12 13:36:39 +01:00
|
|
|
} else {
|
2022-04-28 10:12:16 +02:00
|
|
|
xctx->gridpoint[i].x=(short)(x);
|
|
|
|
|
xctx->gridpoint[i].y=(short)(y);
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-12-12 13:36:39 +01:00
|
|
|
}
|
2022-11-30 00:59:45 +01:00
|
|
|
#endif
|
2020-12-12 13:36:39 +01:00
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2022-11-30 00:59:45 +01:00
|
|
|
#if DRAW_ALL_CAIRO==0
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) {
|
2021-11-10 13:43:08 +01:00
|
|
|
if(big_gr) {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawSegments(display, xctx->window, xctx->gc[GRIDLAYER], xctx->biggridpoint, i);
|
2020-12-12 22:48:25 +01:00
|
|
|
} else {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawPoints(display, xctx->window, xctx->gc[GRIDLAYER], xctx->gridpoint, i, CoordModeOrigin);
|
2020-12-12 22:48:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap) {
|
2021-11-10 13:43:08 +01:00
|
|
|
if(big_gr) {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawSegments(display, xctx->save_pixmap, xctx->gc[GRIDLAYER], xctx->biggridpoint, i);
|
2020-12-12 22:48:25 +01:00
|
|
|
} else {
|
2025-01-21 12:34:14 +01:00
|
|
|
XDrawPoints(display, xctx->save_pixmap, xctx->gc[GRIDLAYER], xctx->gridpoint, i, CoordModeOrigin);
|
2020-12-12 22:48:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-30 00:59:45 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if DRAW_ALL_CAIRO==1
|
|
|
|
|
if(xctx->draw_pixmap) cairo_stroke(xctx->cairo_save_ctx);
|
|
|
|
|
if(xctx->draw_window) cairo_stroke(xctx->cairo_ctx);
|
|
|
|
|
#endif
|
2025-03-07 14:45:13 +01:00
|
|
|
|
|
|
|
|
#if DRAW_ALL_CAIRO==0
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[GRIDLAYER],
|
|
|
|
|
XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-09 15:36:06 +02:00
|
|
|
#if !defined(__unix__) && HAS_CAIRO==1
|
2022-09-25 21:11:52 +02:00
|
|
|
static void my_cairo_drawline(cairo_t *ct, int layer, double x1, double y1, double x2, double y2, int dash)
|
|
|
|
|
{
|
|
|
|
|
cairo_set_source_rgb(ct,
|
|
|
|
|
(double)xctx->xcolor_array[layer].red/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].green/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].blue/65535.0);
|
|
|
|
|
if (dash) {
|
|
|
|
|
double dashes[1];
|
|
|
|
|
dashes[0] = dash;
|
|
|
|
|
cairo_set_dash(ct, dashes, 1, 0);
|
|
|
|
|
}
|
|
|
|
|
cairo_move_to(ct, x1, y1);
|
|
|
|
|
cairo_line_to(ct,x2, y2);
|
|
|
|
|
cairo_stroke(ct); /* This lines need to be here */
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2022-09-25 21:11:52 +02:00
|
|
|
static void my_cairo_drawpoints(cairo_t *ct, int layer, XPoint *points, int npoints)
|
|
|
|
|
{
|
|
|
|
|
cairo_set_source_rgb(ct,
|
|
|
|
|
(double)xctx->xcolor_array[layer].red/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].green/65535.0,
|
|
|
|
|
(double)xctx->xcolor_array[layer].blue/65535.0);
|
|
|
|
|
for (int i =0; i<npoints; ++i) {
|
|
|
|
|
cairo_move_to(ct, points[i].x, points[i].y);
|
|
|
|
|
cairo_rel_line_to(ct,1, 1);
|
|
|
|
|
cairo_stroke(ct); /* This lines need to be here */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void check_cairo_drawline(void *cr, int layer, double x1, double y1, double x2, double y2, int dash)
|
|
|
|
|
{
|
|
|
|
|
if (cr==NULL) return;
|
|
|
|
|
cairo_t *ct = (cairo_t *)cr;
|
|
|
|
|
my_cairo_drawline(cr, layer, x1, y1, x2, y2, dash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void check_cairo_drawpoints(void *cr, int layer, XPoint *points, int npoints)
|
|
|
|
|
{
|
|
|
|
|
if (cr==NULL) return;
|
|
|
|
|
cairo_t *ct = (cairo_t *)cr;
|
|
|
|
|
my_cairo_drawpoints(cr, layer, points, npoints);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-01-21 12:34:14 +01:00
|
|
|
|
2025-01-21 15:38:19 +01:00
|
|
|
void draw_xhair_line(GC gc, int size, double linex1, double liney1, double linex2, double liney2)
|
2025-01-21 12:34:14 +01:00
|
|
|
{
|
|
|
|
|
int big_gr = tclgetboolvar("big_grid_points");
|
|
|
|
|
char dash_arr[2];
|
|
|
|
|
double x1, y1, x2, y2;
|
2025-01-21 15:38:19 +01:00
|
|
|
x1=/* X_TO_SCREEN */ (linex1);
|
|
|
|
|
y1=/* Y_TO_SCREEN */ (liney1);
|
|
|
|
|
x2=/* X_TO_SCREEN */ (linex2);
|
|
|
|
|
y2=/* Y_TO_SCREEN */ (liney2);
|
2025-01-21 12:34:14 +01:00
|
|
|
if( clip(&x1,&y1,&x2,&y2) )
|
|
|
|
|
{
|
|
|
|
|
dash_arr[0] = dash_arr[1] = (char) 3;
|
2025-01-21 15:38:19 +01:00
|
|
|
XSetDashes(display, gc, 0, dash_arr, 1);
|
2025-01-21 12:34:14 +01:00
|
|
|
if(!big_gr) {
|
2025-01-21 15:38:19 +01:00
|
|
|
XSetLineAttributes (display, gc,
|
|
|
|
|
0, size ? LineSolid : xDashType, xCap, xJoin);
|
2025-01-21 12:34:14 +01:00
|
|
|
} else {
|
2025-01-21 15:38:19 +01:00
|
|
|
XSetLineAttributes (display, gc,
|
2025-01-21 16:07:23 +01:00
|
|
|
size ? 0 : XLINEWIDTH(xctx->lw), size ? LineSolid : xDashType, xCap, xJoin);
|
2025-01-21 12:34:14 +01:00
|
|
|
}
|
|
|
|
|
if(xctx->draw_window)
|
2025-01-21 15:38:19 +01:00
|
|
|
XDrawLine(display, xctx->window, gc, (int)x1, (int)y1, (int)x2, (int)y2);
|
2025-01-21 12:34:14 +01:00
|
|
|
if(xctx->draw_pixmap)
|
2025-01-21 15:38:19 +01:00
|
|
|
XDrawLine(display, xctx->save_pixmap, gc, (int)x1, (int)y1, (int)x2, (int)y2);
|
2025-01-21 16:07:23 +01:00
|
|
|
XSetLineAttributes (display, gc,
|
|
|
|
|
XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
2025-01-21 12:34:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 00:47:46 +01:00
|
|
|
void drawline(int c, int what, double linex1, double liney1, double linex2, double liney2,
|
|
|
|
|
double bus, int dash, void *ct)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
static int i = 0;
|
|
|
|
|
#ifndef __unix__
|
|
|
|
|
int j = 0;
|
|
|
|
|
#endif
|
|
|
|
|
static XSegment r[CADDRAWBUFFERSIZE];
|
|
|
|
|
double x1,y1,x2,y2;
|
|
|
|
|
register XSegment *rr;
|
2020-09-02 18:28:20 +02:00
|
|
|
char dash_arr[2];
|
2025-11-29 00:47:46 +01:00
|
|
|
int width;
|
|
|
|
|
|
|
|
|
|
if(bus == -1.0) {
|
|
|
|
|
what = THICK;
|
|
|
|
|
width = INT_BUS_WIDTH(xctx->lw);
|
|
|
|
|
} else if(bus > 0.0) {
|
|
|
|
|
what = NOW;
|
2025-12-01 10:11:19 +01:00
|
|
|
width = XLINEWIDTH(bus * xctx->mooz);
|
2025-11-29 00:47:46 +01:00
|
|
|
} else {
|
|
|
|
|
width = XLINEWIDTH(xctx->lw);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-02 18:28:20 +02:00
|
|
|
|
2020-09-07 14:13:07 +02:00
|
|
|
if(dash && what !=THICK) what = NOW;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
if(!has_x) return;
|
|
|
|
|
rr=r;
|
|
|
|
|
if(what & ADD)
|
|
|
|
|
{
|
|
|
|
|
if(i>=CADDRAWBUFFERSIZE)
|
|
|
|
|
{
|
|
|
|
|
#ifdef __unix__
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XDrawSegments(display, xctx->window, xctx->gc[c], rr,i);
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap)
|
2021-11-16 22:28:10 +01:00
|
|
|
XDrawSegments(display, xctx->save_pixmap, xctx->gc[c], rr,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
#else
|
|
|
|
|
for (j = 0; j < i; ++j) {
|
2021-11-16 22:28:10 +01:00
|
|
|
if (xctx->draw_window)
|
|
|
|
|
XDrawLine(display, xctx->window, xctx->gc[c], rr[j].x1, rr[j].y1, rr[j].x2, rr[j].y2);
|
2021-10-26 00:04:13 +02:00
|
|
|
if (xctx->draw_pixmap)
|
2021-11-16 22:28:10 +01:00
|
|
|
XDrawLine(display, xctx->save_pixmap, xctx->gc[c], rr[j].x1, rr[j].y1, rr[j].x2, rr[j].y2);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
x1=X_TO_SCREEN(linex1);
|
|
|
|
|
y1=Y_TO_SCREEN(liney1);
|
|
|
|
|
x2=X_TO_SCREEN(linex2);
|
|
|
|
|
y2=Y_TO_SCREEN(liney2);
|
|
|
|
|
if( clip(&x1,&y1,&x2,&y2) )
|
|
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
rr[i].x1=(short)x1;
|
2020-08-08 15:47:34 +02:00
|
|
|
rr[i].y1=(short)y1;
|
|
|
|
|
rr[i].x2=(short)x2;
|
|
|
|
|
rr[i].y2=(short)y2;
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & NOW)
|
|
|
|
|
{
|
|
|
|
|
x1=X_TO_SCREEN(linex1);
|
|
|
|
|
y1=Y_TO_SCREEN(liney1);
|
|
|
|
|
x2=X_TO_SCREEN(linex2);
|
|
|
|
|
y2=Y_TO_SCREEN(liney2);
|
|
|
|
|
if( clip(&x1,&y1,&x2,&y2) )
|
|
|
|
|
{
|
2020-09-02 18:28:20 +02:00
|
|
|
if(dash) {
|
2025-11-29 14:36:15 +01:00
|
|
|
dash_arr[0] = dash_arr[1] = (char)dash;
|
2022-12-01 16:30:02 +01:00
|
|
|
XSetDashes(display, xctx->gc[c], 0, dash_arr, 1);
|
2025-11-29 14:36:15 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, xDashType, xCap, xJoin);
|
|
|
|
|
} else if(bus > 0.0) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, CapProjecting, JoinMiter);
|
|
|
|
|
} else if(bus == -1.0) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, LINECAP, LINEJOIN);
|
2020-09-02 18:28:20 +02:00
|
|
|
}
|
2025-11-29 00:47:46 +01:00
|
|
|
|
2022-04-27 13:18:45 +02:00
|
|
|
if(xctx->draw_window) XDrawLine(display, xctx->window, xctx->gc[c], (int)x1, (int)y1, (int)x2, (int)y2);
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap)
|
2022-04-27 13:18:45 +02:00
|
|
|
XDrawLine(display, xctx->save_pixmap, xctx->gc[c], (int)x1, (int)y1, (int)x2, (int)y2);
|
2025-11-30 23:13:38 +01:00
|
|
|
if(dash || bus > 0.0 || bus == -1.0) {
|
2023-09-23 01:46:39 +02:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
2020-09-02 18:28:20 +02:00
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2020-10-12 13:13:31 +02:00
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
else if(what & THICK)
|
|
|
|
|
{
|
2020-08-08 15:47:34 +02:00
|
|
|
x1=X_TO_SCREEN(linex1);
|
|
|
|
|
y1=Y_TO_SCREEN(liney1);
|
|
|
|
|
x2=X_TO_SCREEN(linex2);
|
|
|
|
|
y2=Y_TO_SCREEN(liney2);
|
|
|
|
|
if( clip(&x1,&y1,&x2,&y2) )
|
|
|
|
|
{
|
2020-09-02 18:28:20 +02:00
|
|
|
if(dash) {
|
2022-04-28 10:12:16 +02:00
|
|
|
dash_arr[0] = dash_arr[1] = (char) dash;
|
2022-12-01 16:30:02 +01:00
|
|
|
XSetDashes(display, xctx->gc[c], 0, dash_arr, 1);
|
2025-12-01 10:11:19 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, xDashType, xCap, xJoin);
|
2020-09-02 18:28:20 +02:00
|
|
|
} else {
|
2025-12-01 10:11:19 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, LINECAP, LINEJOIN);
|
2020-09-02 18:28:20 +02:00
|
|
|
}
|
2022-04-27 13:18:45 +02:00
|
|
|
if(xctx->draw_window) XDrawLine(display, xctx->window, xctx->gc[c], (int)x1, (int)y1, (int)x2, (int)y2);
|
|
|
|
|
if(xctx->draw_pixmap) XDrawLine(display, xctx->save_pixmap, xctx->gc[c], (int)x1, (int)y1, (int)x2, (int)y2);
|
2023-09-23 01:46:39 +02:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], XLINEWIDTH(xctx->lw), LineSolid, LINECAP , LINEJOIN);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if((what & END) && i)
|
|
|
|
|
{
|
|
|
|
|
#ifdef __unix__
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XDrawSegments(display, xctx->window, xctx->gc[c], rr,i);
|
|
|
|
|
if(xctx->draw_pixmap) XDrawSegments(display, xctx->save_pixmap, xctx->gc[c], rr,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
#else
|
|
|
|
|
for (j = 0; j < i; ++j) {
|
2021-11-16 22:28:10 +01:00
|
|
|
if (xctx->draw_window)
|
|
|
|
|
XDrawLine(display, xctx->window, xctx->gc[c], rr[j].x1, rr[j].y1, rr[j].x2, rr[j].y2);
|
2021-10-26 00:04:13 +02:00
|
|
|
if (xctx->draw_pixmap)
|
2021-11-16 22:28:10 +01:00
|
|
|
XDrawLine(display, xctx->save_pixmap, xctx->gc[c], rr[j].x1, rr[j].y1, rr[j].x2, rr[j].y2);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void drawtempline(GC gc, int what, double linex1,double liney1,double linex2,double liney2)
|
|
|
|
|
{
|
|
|
|
|
static int i = 0;
|
|
|
|
|
#ifndef __unix__
|
|
|
|
|
int j = 0;
|
|
|
|
|
#endif
|
|
|
|
|
static XSegment r[CADDRAWBUFFERSIZE];
|
|
|
|
|
double x1,y1,x2,y2;
|
|
|
|
|
|
|
|
|
|
if(!has_x) return;
|
2023-09-23 01:46:39 +02:00
|
|
|
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled && what == ADD) what = NOW;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(what & ADD)
|
|
|
|
|
{
|
|
|
|
|
if(i>=CADDRAWBUFFERSIZE)
|
|
|
|
|
{
|
|
|
|
|
#ifdef __unix__
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawSegments(display, xctx->window, gc, r,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
#else
|
|
|
|
|
for (j = 0; j < i; ++j) {
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawLine(display, xctx->window, gc, r[j].x1, r[j].y1, r[j].x2, r[j].y2);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
x1=X_TO_SCREEN(linex1);
|
|
|
|
|
y1=Y_TO_SCREEN(liney1);
|
|
|
|
|
x2=X_TO_SCREEN(linex2);
|
|
|
|
|
y2=Y_TO_SCREEN(liney2);
|
|
|
|
|
if( clip(&x1,&y1,&x2,&y2) )
|
|
|
|
|
{
|
|
|
|
|
r[i].x1=(short)x1;
|
|
|
|
|
r[i].y1=(short)y1;
|
|
|
|
|
r[i].x2=(short)x2;
|
|
|
|
|
r[i].y2=(short)y2;
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & NOW)
|
|
|
|
|
{
|
|
|
|
|
x1=X_TO_SCREEN(linex1);
|
|
|
|
|
y1=Y_TO_SCREEN(liney1);
|
|
|
|
|
x2=X_TO_SCREEN(linex2);
|
|
|
|
|
y2=Y_TO_SCREEN(liney2);
|
|
|
|
|
if( clip(&x1,&y1,&x2,&y2) )
|
|
|
|
|
{
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled) {
|
|
|
|
|
RECTORDER(linex1, liney1, linex2, liney2);
|
|
|
|
|
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
|
|
|
|
|
linex1, liney1, linex2, liney2, linex1, liney1, xctx->lw);
|
|
|
|
|
} else {
|
|
|
|
|
XDrawLine(display, xctx->window, gc, (int)x1, (int)y1, (int)x2, (int)y2);
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2020-10-12 13:13:31 +02:00
|
|
|
}
|
|
|
|
|
else if(what & THICK)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
x1=X_TO_SCREEN(linex1);
|
|
|
|
|
y1=Y_TO_SCREEN(liney1);
|
|
|
|
|
x2=X_TO_SCREEN(linex2);
|
|
|
|
|
y2=Y_TO_SCREEN(liney2);
|
|
|
|
|
if( clip(&x1,&y1,&x2,&y2) )
|
|
|
|
|
{
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled) {
|
2025-04-08 01:11:15 +02:00
|
|
|
RECTORDER(linex1, liney1, linex2, liney2);
|
2023-10-27 23:45:54 +02:00
|
|
|
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
|
|
|
|
|
linex1, liney1, linex2, liney2, linex1, liney1, BUS_WIDTH * xctx->lw);
|
|
|
|
|
} else {
|
|
|
|
|
XSetLineAttributes (display, gc, INT_BUS_WIDTH(xctx->lw), LineSolid, LINECAP , LINEJOIN);
|
|
|
|
|
XDrawLine(display, xctx->window, gc, (int)x1, (int)y1, (int)x2, (int)y2);
|
|
|
|
|
XSetLineAttributes (display, gc, XLINEWIDTH(xctx->lw), LineSolid, LINECAP , LINEJOIN);
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if((what & END) && i)
|
|
|
|
|
{
|
|
|
|
|
#ifdef __unix__
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawSegments(display, xctx->window, gc, r,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
#else
|
|
|
|
|
for (j = 0; j < i; ++j) {
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawLine(display, xctx->window, gc, r[j].x1, r[j].y1, r[j].x2, r[j].y2);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-16 11:35:30 +01:00
|
|
|
void drawtemp_manhattanline(GC gc, int what, double x1, double y1, double x2, double y2, int force_manhattan)
|
2025-03-14 15:25:57 +01:00
|
|
|
{
|
2025-03-16 05:15:40 +01:00
|
|
|
double nl_xx1, nl_yy1, nl_xx2, nl_yy2;
|
2025-03-16 11:35:30 +01:00
|
|
|
if(tclgetboolvar("orthogonal_wiring") && force_manhattan) {
|
2025-03-19 00:50:35 +01:00
|
|
|
recompute_orthogonal_manhattanline(x1, y1, x2, y2);
|
2025-03-14 15:25:57 +01:00
|
|
|
}
|
|
|
|
|
if(xctx->manhattan_lines & 1) {
|
2025-03-16 05:15:40 +01:00
|
|
|
nl_xx1 = x1; nl_yy1 = y1;
|
|
|
|
|
nl_xx2 = x2; nl_yy2 = y2;
|
|
|
|
|
ORDER(nl_xx1,nl_yy1,nl_xx2,nl_yy1);
|
|
|
|
|
drawtempline(gc, what, nl_xx1,nl_yy1,nl_xx2,nl_yy1);
|
|
|
|
|
nl_xx1 = x1; nl_yy1 = y1;
|
|
|
|
|
nl_xx2 = x2; nl_yy2 = y2;
|
|
|
|
|
ORDER(nl_xx2,nl_yy1,nl_xx2,nl_yy2);
|
|
|
|
|
drawtempline(gc, what, nl_xx2,nl_yy1,nl_xx2,nl_yy2);
|
2025-03-14 15:25:57 +01:00
|
|
|
} else if(xctx->manhattan_lines & 2) {
|
2025-03-16 05:15:40 +01:00
|
|
|
nl_xx1 = x1; nl_yy1 = y1;
|
|
|
|
|
nl_xx2 = x2; nl_yy2 = y2;
|
|
|
|
|
ORDER(nl_xx1,nl_yy1,nl_xx1,nl_yy2);
|
|
|
|
|
drawtempline(gc, what, nl_xx1,nl_yy1,nl_xx1,nl_yy2);
|
|
|
|
|
nl_xx1 = x1; nl_yy1 = y1;
|
|
|
|
|
nl_xx2 = x2; nl_yy2 = y2;
|
|
|
|
|
ORDER(nl_xx1,nl_yy2,nl_xx2,nl_yy2);
|
|
|
|
|
drawtempline(gc, what, nl_xx1,nl_yy2,nl_xx2,nl_yy2);
|
2025-03-14 15:25:57 +01:00
|
|
|
} else {
|
2025-03-16 05:15:40 +01:00
|
|
|
nl_xx1 = x1; nl_yy1 = y1;
|
|
|
|
|
nl_xx2 = x2; nl_yy2 = y2;
|
|
|
|
|
ORDER(nl_xx1,nl_yy1,nl_xx2,nl_yy2);
|
|
|
|
|
drawtempline(gc, what, nl_xx1,nl_yy1,nl_xx2,nl_yy2);
|
2025-03-14 15:25:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
void drawtemparc(GC gc, int what, double x, double y, double r, double a, double b)
|
|
|
|
|
{
|
|
|
|
|
static int i=0;
|
2020-10-12 13:13:31 +02:00
|
|
|
static XArc xarc[CADDRAWBUFFERSIZE];
|
2020-08-08 15:47:34 +02:00
|
|
|
double x1, y1, x2, y2; /* arc bbox */
|
|
|
|
|
double xx1, yy1, xx2, yy2; /* complete circle bbox in screen coords */
|
|
|
|
|
|
|
|
|
|
if(!has_x) return;
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled && what == ADD) what = NOW;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(what & ADD)
|
|
|
|
|
{
|
|
|
|
|
if(i>=CADDRAWBUFFERSIZE)
|
|
|
|
|
{
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawArcs(display, xctx->window, gc, xarc,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
xx1=X_TO_SCREEN(x-r);
|
|
|
|
|
yy1=Y_TO_SCREEN(y-r);
|
|
|
|
|
xx2=X_TO_SCREEN(x+r);
|
|
|
|
|
yy2=Y_TO_SCREEN(y+r);
|
|
|
|
|
arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2);
|
|
|
|
|
x1=X_TO_SCREEN(x1);
|
|
|
|
|
y1=Y_TO_SCREEN(y1);
|
|
|
|
|
x2=X_TO_SCREEN(x2);
|
|
|
|
|
y2=Y_TO_SCREEN(y2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
xarc[i].x=(short)xx1;
|
|
|
|
|
xarc[i].y=(short)yy1;
|
|
|
|
|
xarc[i].width= (unsigned short)(xx2 - xx1);
|
|
|
|
|
xarc[i].height=(unsigned short)(yy2 - yy1);
|
2022-04-28 10:12:16 +02:00
|
|
|
xarc[i].angle1 = (short)(a*64);
|
|
|
|
|
xarc[i].angle2 = (short)(b*64);
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & NOW)
|
|
|
|
|
{
|
2023-10-27 23:45:54 +02:00
|
|
|
double sx1, sy1, sx2, sy2;
|
2020-08-08 15:47:34 +02:00
|
|
|
xx1=X_TO_SCREEN(x-r);
|
|
|
|
|
yy1=Y_TO_SCREEN(y-r);
|
|
|
|
|
xx2=X_TO_SCREEN(x+r);
|
|
|
|
|
yy2=Y_TO_SCREEN(y+r);
|
|
|
|
|
arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2);
|
2023-10-27 23:45:54 +02:00
|
|
|
sx1=X_TO_SCREEN(x1);
|
|
|
|
|
sy1=Y_TO_SCREEN(y1);
|
|
|
|
|
sx2=X_TO_SCREEN(x2);
|
|
|
|
|
sy2=Y_TO_SCREEN(y2);
|
|
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&sx1,&sy1,&sx2,&sy2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled) {
|
|
|
|
|
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
|
|
|
|
|
x1, y1, x2, y2, x1, y1, xctx->lw);
|
|
|
|
|
} else {
|
|
|
|
|
XDrawArc(display, xctx->window, gc, (int)xx1, (int)yy1, (int)(xx2-xx1), (int)(yy2-yy1),
|
|
|
|
|
(int)(a*64), (int)(b*64));
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if((what & END) && i)
|
|
|
|
|
{
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawArcs(display, xctx->window, gc, xarc,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* x1,y1: start; x2,y2: end; x3,y3: way point */
|
|
|
|
|
void arc_3_points(double x1, double y1, double x2, double y2, double x3, double y3,
|
|
|
|
|
double *x, double *y, double *r, double *a, double *b)
|
2020-10-12 13:13:31 +02:00
|
|
|
{
|
2020-08-08 15:47:34 +02:00
|
|
|
double A, B, C;
|
|
|
|
|
double c, s;
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
/* s = signed_area, if > 0 : clockwise in xorg coordinate space */
|
|
|
|
|
s = x3*y2-x2*y3 + x2*y1 -x1*y2 + x1*y3-x3*y1;
|
|
|
|
|
A = x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2;
|
|
|
|
|
B = (x1*x1+y1*y1)*(y3-y2)+(x2*x2+y2*y2)*(y1-y3) + (x3*x3+y3*y3)*(y2-y1);
|
|
|
|
|
C = (x1*x1+y1*y1)*(x2-x3)+(x2*x2+y2*y2)*(x3-x1) + (x3*x3+y3*y3)*(x1-x2);
|
|
|
|
|
/* printf("s=%g\n", s); */
|
|
|
|
|
*x = -B/2./A;
|
|
|
|
|
*y = -C/2./A;
|
2020-10-12 13:13:31 +02:00
|
|
|
*r = sqrt( (*x-x1)*(*x-x1) + (*y-y1)*(*y-y1) );
|
2020-08-08 15:47:34 +02:00
|
|
|
*a = fmod(atan2(*y-y1 ,x1-*x )*180./XSCH_PI, 360.);
|
|
|
|
|
if(*a<0.) *a+=360.;
|
|
|
|
|
*b = fmod(atan2(*y-y2 ,x2-*x )*180./XSCH_PI, 360.);
|
|
|
|
|
if(*b<0.) *b+=360.;
|
|
|
|
|
if(s<0.) { /* counter clockwise, P1, P3, P2 */
|
|
|
|
|
*b = fmod(*b-*a, 360.);
|
|
|
|
|
if(*b<0) *b+=360.;
|
|
|
|
|
if(*b==0) *b=360.;
|
|
|
|
|
} else if(s>0.) { /* clockwise, P2, P3, P1 */
|
|
|
|
|
c = fmod(*a-*b, 360.);
|
|
|
|
|
if(c<0) c+=360.;
|
|
|
|
|
if(*b==0) *b=360.;
|
|
|
|
|
*a = *b;
|
|
|
|
|
*b = c;
|
|
|
|
|
} else {
|
|
|
|
|
*r = -1.0; /* no circle thru aligned points */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void filledarc(int c, int what, double x, double y, double r, double a, double b)
|
|
|
|
|
{
|
|
|
|
|
static int i=0;
|
2020-10-12 13:13:31 +02:00
|
|
|
static XArc xarc[CADDRAWBUFFERSIZE];
|
2020-08-08 15:47:34 +02:00
|
|
|
double x1, y1, x2, y2; /* arc bbox */
|
|
|
|
|
double xx1, yy1, xx2, yy2; /* complete circle bbox in screen coords */
|
|
|
|
|
|
|
|
|
|
if(!has_x) return;
|
|
|
|
|
if(what & ADD)
|
|
|
|
|
{
|
|
|
|
|
if(i>=CADDRAWBUFFERSIZE)
|
|
|
|
|
{
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XFillArcs(display, xctx->window, xctx->gc[c], xarc,i);
|
|
|
|
|
if(xctx->draw_pixmap) XFillArcs(display, xctx->save_pixmap, xctx->gc[c], xarc,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
xx1=X_TO_SCREEN(x-r);
|
|
|
|
|
yy1=Y_TO_SCREEN(y-r);
|
|
|
|
|
xx2=X_TO_SCREEN(x+r);
|
|
|
|
|
yy2=Y_TO_SCREEN(y+r);
|
|
|
|
|
arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2);
|
|
|
|
|
x1=X_TO_SCREEN(x1);
|
|
|
|
|
y1=Y_TO_SCREEN(y1);
|
|
|
|
|
x2=X_TO_SCREEN(x2);
|
|
|
|
|
y2=Y_TO_SCREEN(y2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
xarc[i].x=(short)xx1;
|
|
|
|
|
xarc[i].y=(short)yy1;
|
|
|
|
|
xarc[i].width =(unsigned short)(xx2 - xx1);
|
|
|
|
|
xarc[i].height=(unsigned short)(yy2 - yy1);
|
2022-04-28 10:12:16 +02:00
|
|
|
xarc[i].angle1 = (short)(a*64);
|
|
|
|
|
xarc[i].angle2 = (short)(b*64);
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & NOW)
|
|
|
|
|
{
|
|
|
|
|
xx1=X_TO_SCREEN(x-r);
|
|
|
|
|
yy1=Y_TO_SCREEN(y-r);
|
|
|
|
|
xx2=X_TO_SCREEN(x+r);
|
|
|
|
|
yy2=Y_TO_SCREEN(y+r);
|
|
|
|
|
arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2);
|
|
|
|
|
x1=X_TO_SCREEN(x1);
|
|
|
|
|
y1=Y_TO_SCREEN(y1);
|
|
|
|
|
x2=X_TO_SCREEN(x2);
|
|
|
|
|
y2=Y_TO_SCREEN(y2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2022-04-27 13:18:45 +02:00
|
|
|
if(xctx->draw_window) XFillArc(display, xctx->window, xctx->gc[c], (int)xx1, (int)yy1,
|
|
|
|
|
(int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64));
|
|
|
|
|
if(xctx->draw_pixmap) XFillArc(display, xctx->save_pixmap, xctx->gc[c], (int)xx1, (int)yy1,
|
|
|
|
|
(int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64));
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if((what & END) && i)
|
|
|
|
|
{
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XFillArcs(display, xctx->window, xctx->gc[c], xarc,i);
|
|
|
|
|
if(xctx->draw_pixmap) XFillArcs(display, xctx->save_pixmap, xctx->gc[c], xarc,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-28 13:31:45 +01:00
|
|
|
void drawarc(int c, int what, double x, double y, double r, double a, double b, int arc_fill, double bus, int dash)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
static int i=0;
|
2020-10-12 13:13:31 +02:00
|
|
|
static XArc xarc[CADDRAWBUFFERSIZE];
|
2020-08-08 15:47:34 +02:00
|
|
|
double x1, y1, x2, y2; /* arc bbox */
|
|
|
|
|
double xx1, yy1, xx2, yy2; /* complete circle bbox in screen coords */
|
2024-03-09 03:12:25 +01:00
|
|
|
GC gc;
|
2025-11-28 13:31:45 +01:00
|
|
|
int width;
|
|
|
|
|
|
|
|
|
|
if(bus == -1.0) {
|
|
|
|
|
what = NOW;
|
|
|
|
|
width = INT_BUS_WIDTH(xctx->lw);
|
|
|
|
|
} else if(bus > 0.0) {
|
|
|
|
|
what = NOW;
|
2025-12-01 10:11:19 +01:00
|
|
|
width = XLINEWIDTH(bus * xctx->mooz);
|
2025-11-28 13:31:45 +01:00
|
|
|
} else {
|
|
|
|
|
width = XLINEWIDTH(xctx->lw);
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
|
2020-09-02 23:59:58 +02:00
|
|
|
if(arc_fill || dash) what = NOW;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
if(!has_x) return;
|
|
|
|
|
if(what & ADD)
|
|
|
|
|
{
|
|
|
|
|
if(i>=CADDRAWBUFFERSIZE)
|
|
|
|
|
{
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XDrawArcs(display, xctx->window, xctx->gc[c], xarc,i);
|
|
|
|
|
if(xctx->draw_pixmap) XDrawArcs(display, xctx->save_pixmap, xctx->gc[c], xarc,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
xx1=X_TO_SCREEN(x-r);
|
|
|
|
|
yy1=Y_TO_SCREEN(y-r);
|
|
|
|
|
xx2=X_TO_SCREEN(x+r);
|
|
|
|
|
yy2=Y_TO_SCREEN(y+r);
|
|
|
|
|
arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2);
|
|
|
|
|
x1=X_TO_SCREEN(x1);
|
|
|
|
|
y1=Y_TO_SCREEN(y1);
|
|
|
|
|
x2=X_TO_SCREEN(x2);
|
|
|
|
|
y2=Y_TO_SCREEN(y2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
xarc[i].x=(short)xx1;
|
|
|
|
|
xarc[i].y=(short)yy1;
|
|
|
|
|
xarc[i].width =(unsigned short)(xx2 - xx1);
|
|
|
|
|
xarc[i].height=(unsigned short)(yy2 - yy1);
|
2022-04-28 10:12:16 +02:00
|
|
|
xarc[i].angle1 = (short)(a*64);
|
|
|
|
|
xarc[i].angle2 = (short)(b*64);
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & NOW)
|
|
|
|
|
{
|
|
|
|
|
xx1=X_TO_SCREEN(x-r);
|
|
|
|
|
yy1=Y_TO_SCREEN(y-r);
|
|
|
|
|
xx2=X_TO_SCREEN(x+r);
|
|
|
|
|
yy2=Y_TO_SCREEN(y+r);
|
|
|
|
|
if(arc_fill)
|
|
|
|
|
arc_bbox(x, y, r, 0, 360, &x1,&y1,&x2,&y2);
|
|
|
|
|
else
|
|
|
|
|
arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2);
|
|
|
|
|
x1=X_TO_SCREEN(x1);
|
|
|
|
|
y1=Y_TO_SCREEN(y1);
|
|
|
|
|
x2=X_TO_SCREEN(x2);
|
|
|
|
|
y2=Y_TO_SCREEN(y2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2025-11-28 13:31:45 +01:00
|
|
|
|
|
|
|
|
if(dash) {
|
|
|
|
|
char dash_arr[2];
|
|
|
|
|
dash_arr[0] = dash_arr[1] = (char)dash;
|
|
|
|
|
XSetDashes(display, xctx->gc[c], 0, dash_arr, 1);
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, xDashType, xCap, xJoin);
|
|
|
|
|
} else if(bus > 0.0) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, CapProjecting, JoinMiter);
|
|
|
|
|
} else if(bus == -1.0) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, LINECAP, LINEJOIN);
|
|
|
|
|
}
|
2020-09-02 23:59:58 +02:00
|
|
|
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) {
|
2022-04-27 13:18:45 +02:00
|
|
|
XDrawArc(display, xctx->window, xctx->gc[c], (int)xx1, (int)yy1,
|
|
|
|
|
(int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64));
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap) {
|
2022-04-27 13:18:45 +02:00
|
|
|
XDrawArc(display, xctx->save_pixmap, xctx->gc[c], (int)xx1, (int)yy1,
|
|
|
|
|
(int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64));
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-18 02:25:55 +01:00
|
|
|
if(xctx->fill_pattern && (xctx->fill_type[c] || arc_fill == 2) ){
|
2024-03-09 03:12:25 +01:00
|
|
|
|
2025-02-18 02:25:55 +01:00
|
|
|
if(arc_fill == 2) gc = xctx->gc[c];
|
2024-03-09 03:12:25 +01:00
|
|
|
else gc = xctx->gcstipple[c];
|
2020-08-08 15:47:34 +02:00
|
|
|
if(arc_fill) {
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window)
|
2024-03-09 03:12:25 +01:00
|
|
|
XFillArc(display, xctx->window, gc, (int)xx1, (int)yy1,
|
2022-04-27 13:18:45 +02:00
|
|
|
(int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64));
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap)
|
2024-03-09 03:12:25 +01:00
|
|
|
XFillArc(display, xctx->save_pixmap, gc, (int)xx1, (int)yy1,
|
2022-04-27 13:18:45 +02:00
|
|
|
(int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64));
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-11-30 23:13:38 +01:00
|
|
|
if(dash || bus > 0.0 || bus == -1.0) {
|
2025-11-28 13:31:45 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
2020-09-02 23:59:58 +02:00
|
|
|
}
|
2025-11-28 13:31:45 +01:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if((what & END) && i)
|
|
|
|
|
{
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XDrawArcs(display, xctx->window, xctx->gc[c], xarc,i);
|
|
|
|
|
if(xctx->draw_pixmap) XDrawArcs(display, xctx->save_pixmap, xctx->gc[c], xarc,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 01:34:54 +02:00
|
|
|
void filledrect(int c, int what, double rectx1,double recty1,double rectx2,double recty2, int fill,
|
|
|
|
|
int e_a, int e_b)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2024-03-09 03:12:25 +01:00
|
|
|
static int iif = 0, iis = 0;
|
|
|
|
|
int *i;
|
|
|
|
|
static XRectangle rf[CADDRAWBUFFERSIZE]; /* full fill */
|
|
|
|
|
static XRectangle rs[CADDRAWBUFFERSIZE]; /* stippled fill */
|
|
|
|
|
XRectangle *r;
|
2020-08-08 15:47:34 +02:00
|
|
|
double x1,y1,x2,y2;
|
2024-04-05 01:34:54 +02:00
|
|
|
double xx1,yy1,xx2,yy2;
|
2024-03-09 03:12:25 +01:00
|
|
|
GC gc;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
if(!has_x) return;
|
2024-03-09 03:12:25 +01:00
|
|
|
if(!xctx->fill_pattern) return;
|
2025-02-18 02:25:55 +01:00
|
|
|
if(fill != 2 && !xctx->fill_type[c]) return;
|
|
|
|
|
if(fill == 2) { /* full fill */
|
2024-03-09 03:12:25 +01:00
|
|
|
gc = xctx->gc[c];
|
|
|
|
|
r = rf;
|
|
|
|
|
i = &iif;
|
|
|
|
|
} else { /* stippled fill */
|
|
|
|
|
gc = xctx->gcstipple[c];
|
|
|
|
|
r = rs;
|
|
|
|
|
i = &iis;
|
|
|
|
|
}
|
2024-04-05 01:34:54 +02:00
|
|
|
if(e_a != -1) what = NOW;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(what & NOW)
|
|
|
|
|
{
|
2024-04-05 01:34:54 +02:00
|
|
|
xx1 = x1 = X_TO_SCREEN(rectx1);
|
|
|
|
|
yy1 = y1 = Y_TO_SCREEN(recty1);
|
|
|
|
|
xx2 = x2 = X_TO_SCREEN(rectx2);
|
|
|
|
|
yy2 = y2 = Y_TO_SCREEN(recty2);
|
2021-11-17 23:12:17 +01:00
|
|
|
if(!xctx->only_probes && (x2-x1)< 3.0 && (y2-y1)< 3.0) return;
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2024-04-05 01:34:54 +02:00
|
|
|
if(xctx->draw_window) {
|
|
|
|
|
if(e_a != -1) {
|
|
|
|
|
XFillArc(display, xctx->window, gc, (int)xx1, (int)yy1,
|
|
|
|
|
(unsigned int)xx2 - (unsigned int)xx1,
|
|
|
|
|
(unsigned int)yy2 - (unsigned int)yy1, e_a * 64, e_b * 64);
|
|
|
|
|
} else {
|
|
|
|
|
XFillRectangle(display, xctx->window, gc, (int)x1, (int)y1,
|
|
|
|
|
(unsigned int)x2 - (unsigned int)x1,
|
|
|
|
|
(unsigned int)y2 - (unsigned int)y1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(xctx->draw_pixmap) {
|
|
|
|
|
if(e_a != -1) {
|
|
|
|
|
XFillArc(display, xctx->save_pixmap, gc, (int)xx1, (int)yy1,
|
|
|
|
|
(unsigned int)xx2 - (unsigned int)xx1,
|
|
|
|
|
(unsigned int)yy2 - (unsigned int)yy1, e_a * 64, e_b * 64);
|
|
|
|
|
} else {
|
|
|
|
|
XFillRectangle(display, xctx->save_pixmap, gc, (int)x1, (int)y1,
|
|
|
|
|
(unsigned int)x2 - (unsigned int)x1,
|
|
|
|
|
(unsigned int)y2 - (unsigned int)y1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & ADD)
|
|
|
|
|
{
|
2024-03-09 03:12:25 +01:00
|
|
|
if(*i >= CADDRAWBUFFERSIZE)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2024-03-09 03:12:25 +01:00
|
|
|
if(xctx->draw_window) XFillRectangles(display, xctx->window, gc, r, *i);
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap)
|
2024-03-09 03:12:25 +01:00
|
|
|
XFillRectangles(display, xctx->save_pixmap, gc, r, *i);
|
|
|
|
|
*i=0;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
x1=X_TO_SCREEN(rectx1);
|
|
|
|
|
y1=Y_TO_SCREEN(recty1);
|
|
|
|
|
x2=X_TO_SCREEN(rectx2);
|
|
|
|
|
y2=Y_TO_SCREEN(recty2);
|
2021-11-17 23:12:17 +01:00
|
|
|
if(!xctx->only_probes && (x2-x1)< 3.0 && (y2-y1)< 3.0) return;
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2024-03-09 03:12:25 +01:00
|
|
|
r[*i].x=(short)x1;
|
|
|
|
|
r[*i].y=(short)y1;
|
|
|
|
|
r[*i].width=(unsigned short)(x2-r[*i].x);
|
|
|
|
|
r[*i].height=(unsigned short)(y2-r[*i].y);
|
|
|
|
|
++(*i);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-09 03:12:25 +01:00
|
|
|
else if(what & END)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2024-03-09 03:12:25 +01:00
|
|
|
if(iis) {
|
|
|
|
|
if(xctx->draw_window) XFillRectangles(display, xctx->window, xctx->gcstipple[c], rs, iis);
|
|
|
|
|
if(xctx->draw_pixmap) XFillRectangles(display, xctx->save_pixmap, xctx->gcstipple[c], rs ,iis);
|
|
|
|
|
iis = 0;
|
|
|
|
|
}
|
|
|
|
|
if(iif) {
|
|
|
|
|
if(xctx->draw_window) XFillRectangles(display, xctx->window, xctx->gc[c], rf ,iif);
|
|
|
|
|
if(xctx->draw_pixmap) XFillRectangles(display, xctx->save_pixmap, xctx->gc[c], rf ,iif);
|
|
|
|
|
iif = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void polygon_bbox(double *x, double *y, int points, double *bx1, double *by1, double *bx2, double *by2)
|
|
|
|
|
{
|
|
|
|
|
int j;
|
2023-02-18 09:44:11 +01:00
|
|
|
for(j=0; j<points; ++j) {
|
2020-08-08 15:47:34 +02:00
|
|
|
if(j==0 || x[j] < *bx1) *bx1 = x[j];
|
|
|
|
|
if(j==0 || x[j] > *bx2) *bx2 = x[j];
|
2021-12-22 11:11:46 +01:00
|
|
|
if(j==0 || y[j] < *by1) *by1 = y[j];
|
2020-08-08 15:47:34 +02:00
|
|
|
if(j==0 || y[j] > *by2) *by2 = y[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
void arc_bbox(double x, double y, double r, double a, double b,
|
2020-08-08 15:47:34 +02:00
|
|
|
double *bx1, double *by1, double *bx2, double *by2)
|
|
|
|
|
{
|
|
|
|
|
double x2, y2, x3, y3;
|
|
|
|
|
int aa, bb, i;
|
|
|
|
|
|
|
|
|
|
if(b==360.) {
|
|
|
|
|
*bx1 = x-r;
|
|
|
|
|
*by1 = y-r;
|
|
|
|
|
*bx2 = x+r;
|
|
|
|
|
*by2 = y+r;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-10-30 01:12:11 +01:00
|
|
|
if(b < 0.) {
|
2025-10-30 01:45:20 +01:00
|
|
|
double aaa = a;
|
|
|
|
|
a = aaa + b;
|
|
|
|
|
b = -b;
|
2025-10-30 01:12:11 +01:00
|
|
|
}
|
2025-10-30 01:45:20 +01:00
|
|
|
a = fmod(a, 360.);
|
|
|
|
|
if(a<0) a+=360.;
|
2020-08-08 15:47:34 +02:00
|
|
|
aa = (int)(ceil(a/90.))*90;
|
|
|
|
|
bb = (int)(floor((a+b)/90.))*90;
|
|
|
|
|
|
2020-10-12 13:13:31 +02:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
/* printf("arc_bbox(): aa=%d bb=%d\n", aa, bb); */
|
|
|
|
|
x2 = x + r * cos(a * XSCH_PI/180.);
|
|
|
|
|
y2 = y - r * sin(a * XSCH_PI/180.);
|
|
|
|
|
x3 = x + r * cos((a+b) * XSCH_PI/180.);
|
|
|
|
|
y3 = y - r * sin((a+b) * XSCH_PI/180.);
|
|
|
|
|
|
|
|
|
|
/* *bx1 = (x2 < x ) ? x2 : x; */
|
|
|
|
|
*bx1 = x2;
|
|
|
|
|
if(x3 < *bx1) *bx1 = x3;
|
|
|
|
|
/* *bx2 = (x2 > x ) ? x2 : x; */
|
|
|
|
|
*bx2 = x2;
|
|
|
|
|
if(x3 > *bx2) *bx2 = x3;
|
|
|
|
|
/* *by1 = (y2 < y ) ? y2 : y; */
|
|
|
|
|
*by1 = y2;
|
|
|
|
|
if(y3 < *by1) *by1 = y3;
|
|
|
|
|
/* *by2 = (y2 > y ) ? y2 : y; */
|
|
|
|
|
*by2 = y2;
|
|
|
|
|
if(y3 > *by2) *by2 = y3;
|
|
|
|
|
|
2023-02-18 09:44:11 +01:00
|
|
|
for(i=aa; i<=bb; ++i) {
|
2020-08-08 15:47:34 +02:00
|
|
|
if(i%360==0) {
|
|
|
|
|
*bx2 = x + r;
|
|
|
|
|
}
|
|
|
|
|
if(i%360==90) {
|
|
|
|
|
*by1 = y - r;
|
|
|
|
|
}
|
|
|
|
|
if(i%360==180) {
|
|
|
|
|
*bx1 = x - r;
|
|
|
|
|
}
|
|
|
|
|
if(i%360==270) {
|
|
|
|
|
*by2 = y + r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convex Nonconvex Complex */
|
|
|
|
|
#define Polygontype Nonconvex
|
2024-02-27 10:24:21 +01:00
|
|
|
|
2025-02-18 02:25:55 +01:00
|
|
|
/* fill = 1: stippled fill, fill == 2: solid fill */
|
2024-02-29 03:40:08 +01:00
|
|
|
void drawbezier(Drawable w, GC gc, int c, double *x, double *y, int points, int fill)
|
2024-02-27 10:24:21 +01:00
|
|
|
{
|
2024-02-29 11:11:33 +01:00
|
|
|
const double bez_steps = 1.0/32.0; /* divide the t = [0,1] interval into 32 steps */
|
|
|
|
|
static int psize = 1024;
|
2024-02-29 03:40:08 +01:00
|
|
|
static XPoint *p = NULL;
|
2024-02-29 11:11:33 +01:00
|
|
|
int b, i;
|
2024-02-27 10:24:21 +01:00
|
|
|
double t;
|
|
|
|
|
double xp, yp;
|
|
|
|
|
|
2024-02-29 03:40:08 +01:00
|
|
|
double x0, x1, x2, y0, y1, y2;
|
|
|
|
|
|
|
|
|
|
if(points == 0 && x == NULL && y == NULL) { /* cleanup */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &p);
|
2024-03-03 04:47:02 +01:00
|
|
|
return;
|
2024-02-29 03:40:08 +01:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
if(!p) p = my_malloc(_ALLOC_ID_, psize * sizeof(XPoint));
|
2024-02-29 03:40:08 +01:00
|
|
|
i = 0;
|
|
|
|
|
for(b = 0; b < points - 2; b++) {
|
|
|
|
|
if(points == 3) { /* 3 points: only one bezier */
|
|
|
|
|
x0 = x[0];
|
|
|
|
|
y0 = y[0];
|
|
|
|
|
x1 = x[1];
|
|
|
|
|
y1 = y[1];
|
|
|
|
|
x2 = x[2];
|
|
|
|
|
y2 = y[2];
|
|
|
|
|
} else if(b == points - 3) { /* last bezier */
|
|
|
|
|
x0 = (x[points - 3] + x[points - 2]) / 2.0;
|
|
|
|
|
y0 = (y[points - 3] + y[points - 2]) / 2.0;
|
|
|
|
|
x1 = x[points - 2];
|
|
|
|
|
y1 = y[points - 2];
|
|
|
|
|
x2 = x[points - 1];
|
|
|
|
|
y2 = y[points - 1];
|
|
|
|
|
} else if(b == 0) { /* first bezier */
|
|
|
|
|
x0 = x[0];
|
|
|
|
|
y0 = y[0];
|
|
|
|
|
x1 = x[1];
|
|
|
|
|
y1 = y[1];
|
|
|
|
|
x2 = (x[1] + x[2]) / 2.0;
|
|
|
|
|
y2 = (y[1] + y[2]) / 2.0;
|
|
|
|
|
} else { /* beziers in the middle */
|
|
|
|
|
x0 = (x[b] + x[b + 1]) / 2.0;
|
|
|
|
|
y0 = (y[b] + y[b + 1]) / 2.0;
|
|
|
|
|
x1 = x[b + 1];
|
|
|
|
|
y1 = y[b + 1];
|
|
|
|
|
x2 = (x[b + 1] + x[b + 2]) / 2.0;
|
|
|
|
|
y2 = (y[b + 1] + y[b + 2]) / 2.0;
|
|
|
|
|
}
|
2024-02-29 11:11:33 +01:00
|
|
|
for(t = 0; t <= 1.0; t += bez_steps) {
|
|
|
|
|
xp = (1 - t) * (1 - t) * x0 + 2 * (1 - t) * t * x1 + t * t * x2;
|
|
|
|
|
yp = (1 - t) * (1 - t) * y0 + 2 * (1 - t) * t * y1 + t * t * y2;
|
2024-02-29 03:40:08 +01:00
|
|
|
if(i >= psize) {
|
|
|
|
|
psize *= 2;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_realloc(_ALLOC_ID_, &p, psize * sizeof(XPoint));
|
2024-02-29 03:40:08 +01:00
|
|
|
}
|
|
|
|
|
p[i].x = (short)X_TO_SCREEN(xp);
|
|
|
|
|
p[i].y = (short)Y_TO_SCREEN(yp);
|
|
|
|
|
/* dbg(0, "i=%d, p[i].x=%d, p[i].y=%d\n", i, p[i].x, p[i].y); */
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
XDrawLines(display, w, gc, p, i, CoordModeOrigin);
|
2024-03-09 03:12:25 +01:00
|
|
|
if(fill == 1)
|
2024-02-29 03:40:08 +01:00
|
|
|
XFillPolygon(display, w, xctx->gcstipple[c], p, i, Polygontype, CoordModeOrigin);
|
2025-02-18 02:25:55 +01:00
|
|
|
else if(fill==2)
|
2024-03-09 03:12:25 +01:00
|
|
|
XFillPolygon(display, w, xctx->gc[c], p, i, Polygontype, CoordModeOrigin);
|
2024-02-27 10:24:21 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-22 12:22:11 +01:00
|
|
|
/* Unused 'what' parameter used in spice data draw_graph()
|
|
|
|
|
* to avoid unnecessary clipping (what = 0) */
|
2025-11-27 22:03:10 +01:00
|
|
|
void drawpolygon(int c, int what, double *x, double *y, int points, int poly_fill, int dash, double bus, int flags)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
double x1,y1,x2,y2;
|
2024-02-27 10:24:21 +01:00
|
|
|
int fill, bezier;
|
2020-08-08 15:47:34 +02:00
|
|
|
XPoint *p;
|
|
|
|
|
int i;
|
2025-11-27 22:03:10 +01:00
|
|
|
int width = 0;
|
2020-12-05 13:58:44 +01:00
|
|
|
short sx, sy;
|
2024-03-09 03:12:25 +01:00
|
|
|
GC gc;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(!has_x) return;
|
2025-11-27 22:03:10 +01:00
|
|
|
if(bus == -1.0) what = THICK;
|
2025-11-30 23:13:38 +01:00
|
|
|
|
|
|
|
|
if(bus == -1.0) {
|
|
|
|
|
width = INT_BUS_WIDTH(xctx->lw);
|
|
|
|
|
} else if(bus > 0.0) {
|
2025-12-01 10:11:19 +01:00
|
|
|
width = XLINEWIDTH(bus * xctx->mooz);
|
2025-11-30 23:13:38 +01:00
|
|
|
} else {
|
|
|
|
|
width = XLINEWIDTH(xctx->lw);
|
|
|
|
|
}
|
2025-11-27 22:03:10 +01:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
polygon_bbox(x, y, points, &x1,&y1,&x2,&y2);
|
|
|
|
|
x1=X_TO_SCREEN(x1);
|
|
|
|
|
x2=X_TO_SCREEN(x2);
|
2021-12-22 11:11:46 +01:00
|
|
|
y1=Y_TO_SCREEN(y1);
|
2020-08-08 15:47:34 +02:00
|
|
|
y2=Y_TO_SCREEN(y2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( !rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) {
|
2020-08-08 15:47:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2022-11-07 13:34:48 +01:00
|
|
|
if(!xctx->only_probes && (x2-x1)<1.0 && (y2-y1)<1.0) return;
|
2025-04-18 02:02:15 +02:00
|
|
|
p = my_malloc(_ALLOC_ID_, sizeof(XPoint) * points);
|
2021-12-22 12:22:11 +01:00
|
|
|
if(what) {
|
2023-02-18 09:44:11 +01:00
|
|
|
for(i=0;i<points; ++i) {
|
2021-12-22 12:22:11 +01:00
|
|
|
clip_xy_to_short(X_TO_SCREEN(x[i]), Y_TO_SCREEN(y[i]), &sx, &sy);
|
|
|
|
|
p[i].x = sx;
|
|
|
|
|
p[i].y = sy;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* preserve cache locality working on contiguous data */
|
2023-02-18 09:44:11 +01:00
|
|
|
for(i=0;i<points; ++i) p[i].x = (short)X_TO_SCREEN(x[i]);
|
|
|
|
|
for(i=0;i<points; ++i) p[i].y = (short)Y_TO_SCREEN(y[i]);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2025-02-18 02:25:55 +01:00
|
|
|
fill = xctx->fill_pattern && ((xctx->fill_type[c] && poly_fill == 1) || poly_fill == 2 ) &&
|
2024-03-09 03:12:25 +01:00
|
|
|
(x[0] == x[points-1]) && (y[0] == y[points-1]);
|
2024-03-02 02:29:03 +01:00
|
|
|
bezier = (flags & 1) && (points > 2);
|
2025-04-08 01:11:15 +02:00
|
|
|
|
2025-11-27 22:03:10 +01:00
|
|
|
if(dash) {
|
|
|
|
|
char dash_arr[2];
|
|
|
|
|
dash_arr[0] = dash_arr[1] = (char)dash;
|
|
|
|
|
XSetDashes(display, xctx->gc[c], 0, dash_arr, 1);
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, xDashType, xCap, xJoin);
|
|
|
|
|
} else if(bus > 0.0) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, CapProjecting, JoinMiter);
|
2025-11-28 13:31:45 +01:00
|
|
|
} else if(bus == -1.0) {
|
2025-11-27 22:03:10 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, LINECAP, LINEJOIN);
|
2020-09-02 18:28:20 +02:00
|
|
|
}
|
2025-04-08 01:11:15 +02:00
|
|
|
|
2024-02-27 10:24:21 +01:00
|
|
|
if(xctx->draw_window) {
|
|
|
|
|
if(bezier) {
|
2025-02-18 02:25:55 +01:00
|
|
|
drawbezier(xctx->window, xctx->gc[c], c, x, y, points, fill ? poly_fill : 0 );
|
2024-02-27 10:24:21 +01:00
|
|
|
} else {
|
|
|
|
|
XDrawLines(display, xctx->window, xctx->gc[c], p, points, CoordModeOrigin);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-02-27 10:24:21 +01:00
|
|
|
if(xctx->draw_pixmap) {
|
|
|
|
|
if(bezier) {
|
2025-02-18 02:25:55 +01:00
|
|
|
drawbezier(xctx->save_pixmap, xctx->gc[c], c, x, y, points, fill ? poly_fill : 0);
|
2024-02-27 10:24:21 +01:00
|
|
|
} else {
|
|
|
|
|
XDrawLines(display, xctx->save_pixmap, xctx->gc[c], p, points, CoordModeOrigin);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-18 02:25:55 +01:00
|
|
|
if(poly_fill == 2) gc = xctx->gc[c];
|
2024-03-09 03:12:25 +01:00
|
|
|
else gc = xctx->gcstipple[c];
|
2024-02-27 10:24:21 +01:00
|
|
|
if(fill && !bezier) {
|
|
|
|
|
if(xctx->draw_window)
|
2024-03-09 03:12:25 +01:00
|
|
|
XFillPolygon(display, xctx->window, gc, p, points, Polygontype, CoordModeOrigin);
|
2024-02-27 10:24:21 +01:00
|
|
|
if(xctx->draw_pixmap)
|
2024-03-09 03:12:25 +01:00
|
|
|
XFillPolygon(display, xctx->save_pixmap, gc, p, points, Polygontype, CoordModeOrigin);
|
2024-02-27 10:24:21 +01:00
|
|
|
}
|
2025-11-30 23:13:38 +01:00
|
|
|
if(dash || bus > 0.0 || bus == -1.0) {
|
2025-11-27 22:03:10 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
2020-09-02 18:28:20 +02:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &p);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-02 02:29:03 +01:00
|
|
|
/* flags: bit 0: bezier
|
|
|
|
|
* bit 1: draw control point circles */
|
2024-02-27 10:24:21 +01:00
|
|
|
void drawtemppolygon(GC gc, int what, double *x, double *y, int points, int flags)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
double x1,y1,x2,y2;
|
2023-10-27 23:45:54 +02:00
|
|
|
double sx1,sy1,sx2,sy2;
|
2020-08-08 15:47:34 +02:00
|
|
|
XPoint *p;
|
|
|
|
|
int i;
|
2020-12-05 13:58:44 +01:00
|
|
|
short sx, sy;
|
2024-03-02 02:29:03 +01:00
|
|
|
int bezier, drawpoints;
|
2020-08-08 15:47:34 +02:00
|
|
|
if(!has_x) return;
|
|
|
|
|
polygon_bbox(x, y, points, &x1,&y1,&x2,&y2);
|
2023-10-27 23:45:54 +02:00
|
|
|
sx1=X_TO_SCREEN(x1);
|
|
|
|
|
sy1=Y_TO_SCREEN(y1);
|
|
|
|
|
sx2=X_TO_SCREEN(x2);
|
|
|
|
|
sy2=Y_TO_SCREEN(y2);
|
|
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&sx1,&sy1,&sx2,&sy2) ) {
|
|
|
|
|
|
2024-03-05 09:55:35 +01:00
|
|
|
bezier = (flags & 1) && (points > 2);
|
2024-03-02 02:29:03 +01:00
|
|
|
drawpoints = (flags & 2);
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled) {
|
|
|
|
|
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
|
2024-03-01 00:12:44 +01:00
|
|
|
x1 - xctx->cadhalfdotsize, y1 - xctx->cadhalfdotsize,
|
|
|
|
|
x2 + xctx->cadhalfdotsize, y2 + xctx->cadhalfdotsize,
|
|
|
|
|
x1 - xctx->cadhalfdotsize, y1 - xctx->cadhalfdotsize, xctx->lw);
|
2023-10-27 23:45:54 +02:00
|
|
|
} else {
|
2024-03-02 02:29:03 +01:00
|
|
|
if(drawpoints && (gc == xctx->gc[SELLAYER] || gc == xctx->gctiled) ) for(i = 0; i < points; i++) {
|
2024-02-29 11:11:33 +01:00
|
|
|
if( POINTINSIDE(X_TO_SCREEN(x[i]), Y_TO_SCREEN(y[i]), xctx->areax1, xctx->areay1,
|
|
|
|
|
xctx->areax2, xctx->areay2)) {
|
2024-03-01 00:12:44 +01:00
|
|
|
drawtemparc(gc, NOW, x[i], y[i], xctx->cadhalfdotsize, 0., 360.);
|
2024-02-29 11:11:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-02-27 10:24:21 +01:00
|
|
|
if(bezier) {
|
|
|
|
|
drawbezier(xctx->window, gc, 0, x, y, points, 0);
|
|
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
p = my_malloc(_ALLOC_ID_, sizeof(XPoint) * points);
|
2024-02-27 10:24:21 +01:00
|
|
|
for(i=0;i<points; ++i) {
|
|
|
|
|
clip_xy_to_short(X_TO_SCREEN(x[i]), Y_TO_SCREEN(y[i]), &sx, &sy);
|
|
|
|
|
p[i].x = sx;
|
|
|
|
|
p[i].y = sy;
|
|
|
|
|
}
|
|
|
|
|
XDrawLines(display, xctx->window, gc, p, points, CoordModeOrigin);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &p);
|
2023-10-27 23:45:54 +02:00
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 14:36:15 +01:00
|
|
|
void drawrect(int c, int what, double rectx1,double recty1,double rectx2,double recty2, double bus, int dash,
|
2024-04-05 01:34:54 +02:00
|
|
|
int e_a, int e_b)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
static int i=0;
|
|
|
|
|
static XRectangle r[CADDRAWBUFFERSIZE];
|
|
|
|
|
double x1,y1,x2,y2;
|
2024-04-05 01:34:54 +02:00
|
|
|
double xx1,yy1,xx2,yy2;
|
2020-09-02 18:28:20 +02:00
|
|
|
char dash_arr[2];
|
2025-11-29 14:36:15 +01:00
|
|
|
int width;
|
2020-08-08 15:47:34 +02:00
|
|
|
|
|
|
|
|
if(!has_x) return;
|
2025-11-29 14:36:15 +01:00
|
|
|
|
|
|
|
|
if(bus == -1.0) {
|
|
|
|
|
what = NOW;
|
|
|
|
|
width = INT_BUS_WIDTH(xctx->lw);
|
|
|
|
|
} else if(bus > 0.0) {
|
|
|
|
|
what = NOW;
|
2025-12-01 10:11:19 +01:00
|
|
|
width = XLINEWIDTH(bus * xctx->mooz);
|
2025-11-29 14:36:15 +01:00
|
|
|
} else {
|
|
|
|
|
width = XLINEWIDTH(xctx->lw);
|
|
|
|
|
}
|
2020-09-02 18:28:20 +02:00
|
|
|
if(dash) what = NOW;
|
2025-11-29 14:36:15 +01:00
|
|
|
|
2024-04-05 01:34:54 +02:00
|
|
|
if(e_a != -1) what = NOW; /* ellipse */
|
2020-08-08 15:47:34 +02:00
|
|
|
if(what & NOW)
|
|
|
|
|
{
|
2024-04-05 01:34:54 +02:00
|
|
|
xx1 = x1 = X_TO_SCREEN(rectx1);
|
|
|
|
|
yy1 = y1 = Y_TO_SCREEN(recty1);
|
|
|
|
|
xx2 = x2 = X_TO_SCREEN(rectx2);
|
|
|
|
|
yy2 = y2 = Y_TO_SCREEN(recty2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-09-02 18:28:20 +02:00
|
|
|
if(dash) {
|
2022-04-28 10:12:16 +02:00
|
|
|
dash_arr[0] = dash_arr[1] = (char)dash;
|
2022-12-01 16:30:02 +01:00
|
|
|
XSetDashes(display, xctx->gc[c], 0, dash_arr, 1);
|
2025-11-29 14:36:15 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, xDashType, xCap, xJoin);
|
|
|
|
|
} else if(bus > 0.0) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, CapProjecting, JoinMiter);
|
|
|
|
|
} else if(bus == -1.0) {
|
|
|
|
|
XSetLineAttributes (display, xctx->gc[c], width, LineSolid, LINECAP, LINEJOIN);
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 01:34:54 +02:00
|
|
|
if(xctx->draw_window) {
|
|
|
|
|
if(e_a != -1) {
|
|
|
|
|
XDrawArc(display, xctx->window, xctx->gc[c], (int)xx1, (int)yy1,
|
|
|
|
|
(unsigned int)xx2 - (unsigned int)xx1,
|
|
|
|
|
(unsigned int)yy2 - (unsigned int)yy1, e_a * 64, e_b * 64);
|
|
|
|
|
} else {
|
|
|
|
|
XDrawRectangle(display, xctx->window, xctx->gc[c], (int)x1, (int)y1,
|
|
|
|
|
(unsigned int)x2 - (unsigned int)x1,
|
|
|
|
|
(unsigned int)y2 - (unsigned int)y1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2024-04-05 01:34:54 +02:00
|
|
|
if(e_a != -1) {
|
|
|
|
|
XDrawArc(display, xctx->save_pixmap, xctx->gc[c], (int)xx1, (int)yy1,
|
|
|
|
|
(unsigned int)xx2 - (unsigned int)xx1,
|
|
|
|
|
(unsigned int)yy2 - (unsigned int)yy1, e_a * 64, e_b * 64);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
XDrawRectangle(display, xctx->save_pixmap, xctx->gc[c], (int)x1, (int)y1,
|
|
|
|
|
(unsigned int)x2 - (unsigned int)x1,
|
|
|
|
|
(unsigned int)y2 - (unsigned int)y1);
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2025-11-30 23:13:38 +01:00
|
|
|
if(dash || bus > 0.0 || bus == -1.0) {
|
2025-11-29 14:36:15 +01:00
|
|
|
XSetLineAttributes (display, xctx->gc[c], XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
2020-09-02 18:28:20 +02:00
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & ADD)
|
|
|
|
|
{
|
|
|
|
|
if(i>=CADDRAWBUFFERSIZE)
|
|
|
|
|
{
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XDrawRectangles(display, xctx->window, xctx->gc[c], r,i);
|
2021-10-26 00:04:13 +02:00
|
|
|
if(xctx->draw_pixmap)
|
2021-11-16 22:28:10 +01:00
|
|
|
XDrawRectangles(display, xctx->save_pixmap, xctx->gc[c], r,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
x1=X_TO_SCREEN(rectx1);
|
|
|
|
|
y1=Y_TO_SCREEN(recty1);
|
|
|
|
|
x2=X_TO_SCREEN(rectx2);
|
|
|
|
|
y2=Y_TO_SCREEN(recty2);
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
r[i].x=(short)x1;
|
2020-08-08 15:47:34 +02:00
|
|
|
r[i].y=(short)y1;
|
2022-04-28 10:12:16 +02:00
|
|
|
r[i].width=(unsigned short)(x2-r[i].x);
|
|
|
|
|
r[i].height=(unsigned short)(y2-r[i].y);
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if((what & END) && i)
|
|
|
|
|
{
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window) XDrawRectangles(display, xctx->window, xctx->gc[c], r,i);
|
|
|
|
|
if(xctx->draw_pixmap) XDrawRectangles(display, xctx->save_pixmap, xctx->gc[c], r,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void drawtemprect(GC gc, int what, double rectx1,double recty1,double rectx2,double recty2)
|
|
|
|
|
{
|
|
|
|
|
static int i=0;
|
|
|
|
|
static XRectangle r[CADDRAWBUFFERSIZE];
|
|
|
|
|
double x1,y1,x2,y2;
|
|
|
|
|
|
|
|
|
|
if(!has_x) return;
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled && what == ADD) what = NOW;
|
2023-09-23 01:46:39 +02:00
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
if(what & NOW)
|
|
|
|
|
{
|
|
|
|
|
x1=X_TO_SCREEN(rectx1);
|
|
|
|
|
y1=Y_TO_SCREEN(recty1);
|
|
|
|
|
x2=X_TO_SCREEN(rectx2);
|
|
|
|
|
y2=Y_TO_SCREEN(recty2);
|
2020-12-31 18:06:54 +01:00
|
|
|
/* if( (x2-x1)< 3.0 && (y2-y1)< 3.0) return; */
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2023-10-27 23:45:54 +02:00
|
|
|
if((fix_broken_tiled_fill || !_unix) && gc == xctx->gctiled) {
|
2023-10-30 22:47:25 +01:00
|
|
|
/*
|
|
|
|
|
* MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
|
|
|
|
|
* rectx1, recty1, rectx2, recty2, rectx1, recty1, xctx->lw);
|
|
|
|
|
*/
|
|
|
|
|
fix_restore_rect(rectx1, recty1, rectx2, recty2);
|
2023-10-27 23:45:54 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
XDrawRectangle(display, xctx->window, gc, (int)x1, (int)y1,
|
|
|
|
|
(unsigned int)x2 - (unsigned int)x1,
|
|
|
|
|
(unsigned int)y2 - (unsigned int)y1);
|
|
|
|
|
}
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(what & ADD)
|
|
|
|
|
{
|
|
|
|
|
if(i>=CADDRAWBUFFERSIZE)
|
|
|
|
|
{
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawRectangles(display, xctx->window, gc, r,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
x1=X_TO_SCREEN(rectx1);
|
|
|
|
|
y1=Y_TO_SCREEN(recty1);
|
|
|
|
|
x2=X_TO_SCREEN(rectx2);
|
|
|
|
|
y2=Y_TO_SCREEN(recty2);
|
2020-12-31 18:06:54 +01:00
|
|
|
/* if( (x2-x1)< 3.0 && (y2-y1)< 3.0) return; */
|
2020-12-02 15:10:47 +01:00
|
|
|
if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) )
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
2020-10-12 13:13:31 +02:00
|
|
|
r[i].x=(short)x1;
|
2020-08-08 15:47:34 +02:00
|
|
|
r[i].y=(short)y1;
|
2022-04-28 10:12:16 +02:00
|
|
|
r[i].width=(unsigned short)(x2-r[i].x);
|
|
|
|
|
r[i].height=(unsigned short)(y2-r[i].y);
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if((what & END) && i)
|
|
|
|
|
{
|
2020-12-03 04:20:05 +01:00
|
|
|
XDrawRectangles(display, xctx->window, gc, r,i);
|
2020-08-08 15:47:34 +02:00
|
|
|
i=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
/* round to closest 1e-ee. 2e-ee, 5e-ee
|
|
|
|
|
* example: delta = 0.234 --> 0.2
|
|
|
|
|
* 3.23 --> 5
|
|
|
|
|
* 66 --> 100
|
|
|
|
|
* 4.3 --> 5
|
|
|
|
|
* 13 --> 20
|
|
|
|
|
* 112 --> 100
|
|
|
|
|
* 6300 --> 10000
|
|
|
|
|
*/
|
2022-02-02 18:33:16 +01:00
|
|
|
static double axis_increment(double a, double b, int div, int freq)
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
{
|
|
|
|
|
double scale;
|
|
|
|
|
double sign;
|
|
|
|
|
double scaled_delta;
|
2021-12-24 23:15:36 +01:00
|
|
|
double delta = b - a;
|
|
|
|
|
if(div == 1) {
|
|
|
|
|
return delta;
|
|
|
|
|
}
|
2022-08-03 10:44:34 +02:00
|
|
|
if(div < 1) div = 1;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
if(delta == 0.0) return delta;
|
2021-12-24 23:15:36 +01:00
|
|
|
/* if user wants only one division, just do what user asks */
|
|
|
|
|
if(div == 1) return delta;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
delta /= div;
|
|
|
|
|
sign = (delta < 0.0) ? -1.0 : 1.0;
|
|
|
|
|
delta = fabs(delta);
|
2022-08-08 01:18:42 +02:00
|
|
|
scale = pow(10.0, floor(mylog10(delta)));
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
scaled_delta = delta / scale; /* 1 <= scaled_delta < 10 */
|
2022-02-02 18:33:16 +01:00
|
|
|
dbg(1, "a=%g, b=%g, scale=%g, scaled_delta=%g --> ", a, b, scale, scaled_delta);
|
|
|
|
|
if(freq && scaled_delta > 2.5) scaled_delta = 10.0;
|
|
|
|
|
else if(freq && scaled_delta > 1.9) scaled_delta = 5.0;
|
|
|
|
|
else if(freq && scaled_delta > 1.4) scaled_delta = 2.0;
|
|
|
|
|
else if(freq) scaled_delta = 1.0;
|
|
|
|
|
else if(scaled_delta > 5.5) scaled_delta = 10.0;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
else if(scaled_delta > 2.2) scaled_delta = 5.0;
|
|
|
|
|
else if(scaled_delta > 1.1) scaled_delta = 2.0;
|
|
|
|
|
else scaled_delta = 1.0;
|
2022-02-02 18:33:16 +01:00
|
|
|
dbg(1, "scaled_delta = %g, scaled_delta * scale * sign=%g\n", scaled_delta, scaled_delta * scale * sign);
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
return scaled_delta * scale * sign;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-29 05:11:39 +01:00
|
|
|
static double axis_start(double n, double delta, int div)
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
{
|
2022-08-03 10:44:34 +02:00
|
|
|
if(div < 1) div = 1;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
if(delta == 0.0) return n;
|
2021-12-24 23:15:36 +01:00
|
|
|
/* if user wants only one division, just do what user asks */
|
|
|
|
|
if(div == 1) return n;
|
2022-10-13 17:36:42 +02:00
|
|
|
if(delta < 0.0) return ceil(n / delta) * delta;
|
|
|
|
|
return floor(n / delta) * delta;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-29 05:11:39 +01:00
|
|
|
static int axis_end(double x, double delta, double b)
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
{
|
|
|
|
|
if(delta == 0.0) return 1; /* guard against infinite loops */
|
|
|
|
|
if(delta > 0) return x > b + delta / 100000.0;
|
|
|
|
|
return x < b + delta / 100000.0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-13 17:36:42 +02:00
|
|
|
static int axis_within_range(double x, double a, double b, double delta, int subdiv)
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
{
|
2022-10-13 17:36:42 +02:00
|
|
|
double eps;
|
|
|
|
|
if(subdiv == 0) subdiv = 1;
|
|
|
|
|
eps = delta / (double) subdiv / 100.0;
|
|
|
|
|
if(a < b) return x >= a - eps;
|
|
|
|
|
return x <= a + eps;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double get_unit(const char *val)
|
|
|
|
|
{
|
|
|
|
|
if(!val) return 1.0;
|
2022-09-27 10:09:51 +02:00
|
|
|
else if(val[0] == 'f') return 1e15;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
else if(val[0] == 'p') return 1e12;
|
|
|
|
|
else if(val[0] == 'n') return 1e9;
|
|
|
|
|
else if(val[0] == 'u') return 1e6;
|
|
|
|
|
else if(val[0] == 'm') return 1e3;
|
|
|
|
|
else if(val[0] == 'k') return 1e-3;
|
|
|
|
|
else if(val[0] == 'M') return 1e-6;
|
|
|
|
|
else if(val[0] == 'G') return 1e-9;
|
2022-09-27 10:09:51 +02:00
|
|
|
else if(val[0] == 'T') return 1e-12;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
return 1.0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-21 11:25:45 +02:00
|
|
|
/* return hierarchy level where raw file was loaded (so may include top level 0) or -1
|
|
|
|
|
* if there is no matching schematic name up in the hierarchy */
|
|
|
|
|
int sch_waves_loaded(void)
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
{
|
2021-12-25 15:01:22 +01:00
|
|
|
int i;
|
2023-10-11 14:05:27 +02:00
|
|
|
if(!xctx->raw || xctx->raw->level == -1) return -1;
|
|
|
|
|
else if(xctx->raw && xctx->raw->values && xctx->raw->names && xctx->raw->schname) {
|
|
|
|
|
dbg(1, "sch_waves_loaded(): raw->schname=%s\n", xctx->raw->schname);
|
2021-12-26 00:33:57 +01:00
|
|
|
for(i = xctx->currsch; i >= 0; i--) {
|
2022-09-21 17:24:16 +02:00
|
|
|
dbg(1, "sch_waves_loaded(): %d --> %s\n", i, xctx->sch[i]);
|
2023-09-11 09:24:03 +02:00
|
|
|
if( !xctx->sch[i] ) continue;
|
2023-10-11 14:05:27 +02:00
|
|
|
if( !strcmp(xctx->raw->schname, xctx->sch[i]) ) {
|
2022-09-21 17:24:16 +02:00
|
|
|
dbg(1, "sch_waves_loaded(): returning %d\n", i);
|
|
|
|
|
return i;
|
|
|
|
|
}
|
2021-12-25 15:01:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-21 11:25:45 +02:00
|
|
|
return -1;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-30 19:20:51 +01:00
|
|
|
static void get_bus_value(int n_bits, int hex_digits, SPICE_DATA **idx_arr, int p, char *busval,
|
2022-01-07 01:54:41 +01:00
|
|
|
double vthl, double vthh)
|
2022-01-04 05:54:25 +01:00
|
|
|
{
|
|
|
|
|
double val;
|
|
|
|
|
int i;
|
|
|
|
|
int hexdigit = 0;
|
|
|
|
|
int bin = 0;
|
|
|
|
|
int hex = 0;
|
2022-01-06 12:57:31 +01:00
|
|
|
char hexstr[] = "084C2A6E195D3B7F"; /* mirrored (Left/right) hex */
|
2022-01-07 01:54:41 +01:00
|
|
|
int x = 0;
|
2022-01-04 05:54:25 +01:00
|
|
|
for(i = n_bits - 1; i >= 0; i--) {
|
2024-03-30 19:20:51 +01:00
|
|
|
if(idx_arr[i]) val = idx_arr[i][p];
|
|
|
|
|
else val = 0.0; /* undefined bus element */
|
2022-01-07 01:54:41 +01:00
|
|
|
if(val >= vthl && val <= vthh) { /* signal transitioning --> 'X' */
|
|
|
|
|
x = 1; /* flag for 'X' value */
|
|
|
|
|
i += bin - 3;
|
|
|
|
|
bin = 3; /* skip remaining bits of hex digit */
|
|
|
|
|
if(i < 0) break; /* MSB nibble is less than 4 bits --> break */
|
|
|
|
|
} else hexdigit |= (val >= vthh ? 1 : 0);
|
2022-01-06 03:49:18 +01:00
|
|
|
if(bin < 3) {
|
2023-02-18 09:44:11 +01:00
|
|
|
++bin;
|
2022-01-06 03:49:18 +01:00
|
|
|
hexdigit <<= 1;
|
|
|
|
|
} else {
|
2023-02-18 09:44:11 +01:00
|
|
|
++hex;
|
2022-01-07 01:54:41 +01:00
|
|
|
if(x)
|
|
|
|
|
busval[hex_digits - hex] = 'X';
|
|
|
|
|
else
|
|
|
|
|
busval[hex_digits - hex] = hexstr[hexdigit];
|
|
|
|
|
hexdigit = 0; /* prepare for next hex digit */
|
2022-01-04 05:54:25 +01:00
|
|
|
bin = 0;
|
2022-01-07 01:54:41 +01:00
|
|
|
x = 0;
|
2022-01-06 03:49:18 +01:00
|
|
|
}
|
2022-01-04 05:54:25 +01:00
|
|
|
}
|
2022-01-07 01:54:41 +01:00
|
|
|
if(bin) { /* process (incomplete) MSB nibble */
|
2023-02-18 09:44:11 +01:00
|
|
|
++hex;
|
2022-01-07 01:54:41 +01:00
|
|
|
if(x)
|
|
|
|
|
busval[hex_digits - hex] = 'X';
|
2022-02-16 02:27:57 +01:00
|
|
|
else {
|
|
|
|
|
hexdigit <<= (3 - bin);
|
2022-01-07 01:54:41 +01:00
|
|
|
busval[hex_digits - hex] = hexstr[hexdigit];
|
2022-02-16 02:27:57 +01:00
|
|
|
}
|
2022-01-04 05:54:25 +01:00
|
|
|
}
|
|
|
|
|
busval[hex_digits] = '\0';
|
|
|
|
|
}
|
2024-03-30 19:20:51 +01:00
|
|
|
|
|
|
|
|
|
2022-01-04 05:54:25 +01:00
|
|
|
/* idx_arr malloc-ated and returned, caller must free! */
|
2024-03-30 19:20:51 +01:00
|
|
|
static SPICE_DATA **get_bus_idx_array(const char *ntok, int *n_bits)
|
2022-01-04 05:54:25 +01:00
|
|
|
{
|
2024-03-30 19:20:51 +01:00
|
|
|
SPICE_DATA **idx_arr =NULL;
|
2022-01-04 05:54:25 +01:00
|
|
|
int p;
|
2022-02-13 22:54:28 +01:00
|
|
|
char *saven, *nptr, *ntok_copy = NULL;
|
2022-01-04 05:54:25 +01:00
|
|
|
const char *bit_name;
|
2022-02-13 22:54:28 +01:00
|
|
|
*n_bits = count_items(ntok, ";,", "") - 1;
|
2023-10-11 14:05:27 +02:00
|
|
|
dbg(1, "get_bus_idx_array(): ntok=%s\n", ntok);
|
|
|
|
|
dbg(1, "get_bus_idx_array(): *n_bits=%d\n", *n_bits);
|
2025-04-18 02:02:15 +02:00
|
|
|
idx_arr = my_malloc(_ALLOC_ID_, (*n_bits) * sizeof(SPICE_DATA *));
|
2022-01-04 05:54:25 +01:00
|
|
|
p = 0;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &ntok_copy, ntok);
|
2022-02-13 22:54:28 +01:00
|
|
|
nptr = ntok_copy;
|
2023-10-11 14:05:27 +02:00
|
|
|
my_strtok_r(nptr, ";,", "", 0, &saven); /*strip off bus name (1st field) */
|
2025-09-23 03:19:57 +02:00
|
|
|
while( (bit_name = my_strtok_r(NULL, ";, \\\n", "", 0, &saven)) ) {
|
2022-01-04 05:54:25 +01:00
|
|
|
int idx;
|
2022-09-10 01:39:30 +02:00
|
|
|
if(p >= *n_bits) break; /* security check to avoid out of bound writing */
|
2024-03-30 19:20:51 +01:00
|
|
|
if( (idx = get_raw_index(bit_name, NULL)) != -1) {
|
|
|
|
|
idx_arr[p] = xctx->raw->values[idx];
|
|
|
|
|
} else {
|
|
|
|
|
idx_arr[p] = NULL;
|
|
|
|
|
}
|
2022-09-10 01:39:30 +02:00
|
|
|
/* dbg(0, "get_bus_idx_array(): bit_name=%s, p=%d\n", bit_name, p); */
|
2023-02-18 09:44:11 +01:00
|
|
|
++p;
|
2022-01-04 05:54:25 +01:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &ntok_copy);
|
2022-01-04 05:54:25 +01:00
|
|
|
return idx_arr;
|
|
|
|
|
}
|
2022-01-01 15:46:03 +01:00
|
|
|
|
2022-02-07 16:14:52 +01:00
|
|
|
/* what == 1: set thick lines,
|
|
|
|
|
* what == 0: restore default
|
|
|
|
|
*/
|
|
|
|
|
static void set_thick_waves(int what, int wcnt, int wave_col, Graph_ctx *gr)
|
|
|
|
|
{
|
2024-12-19 23:53:11 +01:00
|
|
|
unsigned long valuemask;
|
|
|
|
|
XGCValues values;
|
|
|
|
|
valuemask = GCLineWidth;
|
2023-10-13 18:46:15 +02:00
|
|
|
dbg(1, "set_thick_waves(): what=%d\n", what);
|
2022-02-07 16:14:52 +01:00
|
|
|
if(what) {
|
2024-12-19 23:53:11 +01:00
|
|
|
if(gr->hilight_wave == wcnt) {
|
2025-09-20 21:58:36 +02:00
|
|
|
int min = (int) tk_scaling * 2;
|
2024-12-19 23:53:11 +01:00
|
|
|
values.line_width = XLINEWIDTH(2.4 * gr->linewidth_mult * xctx->lw);
|
2025-09-20 21:58:36 +02:00
|
|
|
if(values.line_width < min) values.line_width = min;
|
2024-12-19 23:53:11 +01:00
|
|
|
XChangeGC(display, xctx->gc[wave_col], valuemask, &values);
|
|
|
|
|
}
|
2022-02-07 16:14:52 +01:00
|
|
|
} else {
|
2024-12-19 23:53:11 +01:00
|
|
|
if(gr->hilight_wave == wcnt) {
|
|
|
|
|
values.line_width = XLINEWIDTH(gr->linewidth_mult * xctx->lw);
|
|
|
|
|
XChangeGC(display, xctx->gc[wave_col], valuemask, &values);
|
|
|
|
|
}
|
2022-02-07 16:14:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-17 00:42:31 +02:00
|
|
|
int graph_fullxzoom(int i, Graph_ctx *gr, int dataset)
|
2023-02-26 13:07:14 +01:00
|
|
|
{
|
2023-10-17 00:42:31 +02:00
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
2023-02-26 13:39:57 +01:00
|
|
|
if( sch_waves_loaded() >= 0) {
|
2023-11-29 01:26:45 +01:00
|
|
|
int idx, need_redraw = 0;
|
2023-02-26 13:39:57 +01:00
|
|
|
double xx1, xx2;
|
|
|
|
|
int dset = dataset == -1 ? 0 : dataset;
|
2023-10-17 00:42:31 +02:00
|
|
|
char *custom_rawfile = NULL; /* "rawfile" attr. set in graph: load and switch to specified raw */
|
|
|
|
|
char *sim_type = NULL;
|
2023-10-18 16:10:08 +02:00
|
|
|
int k, save_datasets = -1, save_npoints = -1;
|
2024-06-23 15:59:22 +02:00
|
|
|
int autoload = 0;
|
2023-10-17 14:20:34 +02:00
|
|
|
Raw *raw = NULL;
|
2024-09-06 16:57:13 +02:00
|
|
|
const char *ptr;
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2025-01-12 00:08:41 +01:00
|
|
|
raw = xctx->raw;
|
2025-04-14 12:31:59 +02:00
|
|
|
autoload = !strboolcmp(get_tok_value(r->prop_ptr,"autoload", 0), "true");
|
2024-06-23 15:59:22 +02:00
|
|
|
if(autoload == 0) autoload = 2;
|
2024-11-25 14:29:19 +01:00
|
|
|
ptr = get_tok_value(r->prop_ptr,"rawfile", 0);
|
2024-09-06 16:57:13 +02:00
|
|
|
if(!ptr[0]) {
|
2025-04-18 02:02:15 +02:00
|
|
|
if(raw && raw->rawfile) my_strdup2(_ALLOC_ID_, &custom_rawfile, raw->rawfile);
|
|
|
|
|
else my_strdup2(_ALLOC_ID_, &custom_rawfile, "");
|
2024-09-06 16:57:13 +02:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &custom_rawfile, ptr);
|
2024-09-06 16:57:13 +02:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &sim_type, get_tok_value(r->prop_ptr,"sim_type", 0));
|
2023-10-19 10:47:01 +02:00
|
|
|
if((i == xctx->graph_master) && custom_rawfile[0]) {
|
2024-06-23 15:59:22 +02:00
|
|
|
extra_rawfile(autoload, custom_rawfile, sim_type[0] ? sim_type : xctx->raw->sim_type, -1.0, -1.0);
|
2023-10-17 00:42:31 +02:00
|
|
|
}
|
2024-02-25 12:21:13 +01:00
|
|
|
idx = get_raw_index(find_nth(get_tok_value(r->prop_ptr, "sweep", 0), ", ", "\"", 0, 1), NULL);
|
2023-11-29 01:26:45 +01:00
|
|
|
dbg(1, "graph_fullxzoom(): sweep idx=%d\n", idx);
|
|
|
|
|
if(idx < 0 ) idx = 0;
|
2023-10-17 00:42:31 +02:00
|
|
|
if(i != xctx->graph_master ) {
|
2024-09-06 16:57:13 +02:00
|
|
|
|
2024-11-25 14:29:19 +01:00
|
|
|
ptr = get_tok_value(xctx->rect[GRIDLAYER][xctx->graph_master].prop_ptr,"rawfile", 0);
|
2024-09-06 16:57:13 +02:00
|
|
|
if(!ptr[0]) {
|
2025-04-18 02:02:15 +02:00
|
|
|
if(raw && raw->rawfile) my_strdup2(_ALLOC_ID_, &custom_rawfile, raw->rawfile);
|
|
|
|
|
else my_strdup2(_ALLOC_ID_, &custom_rawfile, "");
|
2024-09-06 16:57:13 +02:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &custom_rawfile, ptr);
|
2024-09-06 16:57:13 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &sim_type,
|
2024-11-25 14:29:19 +01:00
|
|
|
get_tok_value(xctx->rect[GRIDLAYER][xctx->graph_master].prop_ptr,"sim_type", 0));
|
2023-10-19 10:47:01 +02:00
|
|
|
if(custom_rawfile[0]) {
|
2024-06-23 15:59:22 +02:00
|
|
|
extra_rawfile(autoload, custom_rawfile, sim_type[0] ? sim_type : xctx->raw->sim_type, -1.0, -1.0);
|
2023-10-17 00:42:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-10-23 17:20:51 +02:00
|
|
|
|
|
|
|
|
/* if doing double variable DC sweeps raw->datasets == 1 but there are multiple curves.
|
|
|
|
|
* find_closest_wave() may return a dataset number that is > 0 but its a "virtual" dataset
|
|
|
|
|
* since double DC sweeps just do multiple sweeps by wrapping the sweep variable
|
|
|
|
|
*/
|
|
|
|
|
if(dset >= raw->datasets) dset = 0;
|
2023-10-17 14:20:34 +02:00
|
|
|
|
|
|
|
|
/* transform multiple OP points into a dc sweep */
|
|
|
|
|
if(raw && raw->sim_type && !strcmp(raw->sim_type, "op") && raw->datasets > 1 && raw->npoints[0] == 1) {
|
|
|
|
|
save_datasets = raw->datasets;
|
|
|
|
|
raw->datasets = 1;
|
|
|
|
|
save_npoints = raw->npoints[0];
|
|
|
|
|
raw->npoints[0] = raw->allpoints;
|
|
|
|
|
}
|
2023-10-18 16:10:08 +02:00
|
|
|
xx1 = xx2 = get_raw_value(dset, idx, 0);
|
|
|
|
|
for(k = 0; k < xctx->raw->npoints[dset]; k++) {
|
|
|
|
|
double v = get_raw_value(dset, idx, k);
|
|
|
|
|
if(v < xx1) xx1 = v;
|
|
|
|
|
if(v > xx2) xx2 = v;
|
|
|
|
|
}
|
2023-02-26 13:39:57 +01:00
|
|
|
if(gr->logx) {
|
|
|
|
|
xx1 = mylog10(xx1);
|
|
|
|
|
xx2 = mylog10(xx2);
|
|
|
|
|
}
|
2025-01-12 00:08:41 +01:00
|
|
|
dbg(1, "graph_fullxzoom(): xx1=%g, xx2=%g\n");
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1)));
|
|
|
|
|
my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2)));
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2023-12-31 20:33:08 +01:00
|
|
|
if(sch_waves_loaded()!= -1 && custom_rawfile[0]) extra_rawfile(5, NULL, NULL, -1.0, -1.0);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &custom_rawfile);
|
|
|
|
|
my_free(_ALLOC_ID_, &sim_type);
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2023-02-26 13:39:57 +01:00
|
|
|
need_redraw = 1;
|
2023-10-17 14:20:34 +02:00
|
|
|
if(save_npoints != -1) { /* restore multiple OP points from artificial dc sweep */
|
|
|
|
|
raw->datasets = save_datasets;
|
|
|
|
|
raw->npoints[0] = save_npoints;
|
|
|
|
|
}
|
2023-02-26 13:39:57 +01:00
|
|
|
return need_redraw;
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2023-02-26 13:07:14 +01:00
|
|
|
}
|
|
|
|
|
|
2023-10-17 00:42:31 +02:00
|
|
|
int graph_fullyzoom(xRect *r, Graph_ctx *gr, int graph_dataset)
|
2023-02-26 13:07:14 +01:00
|
|
|
{
|
|
|
|
|
int need_redraw = 0;
|
2023-02-26 13:56:52 +01:00
|
|
|
if( sch_waves_loaded() >= 0) {
|
|
|
|
|
if(!gr->digital) {
|
|
|
|
|
int dset;
|
|
|
|
|
int p, v;
|
2023-11-17 01:20:52 +01:00
|
|
|
char *bus_msb = NULL;
|
2023-02-26 13:56:52 +01:00
|
|
|
int sweep_idx = 0;
|
|
|
|
|
double val, start, end;
|
|
|
|
|
double min=0.0, max=0.0;
|
2023-10-11 14:05:27 +02:00
|
|
|
int firstyval = 1;
|
2023-02-26 13:56:52 +01:00
|
|
|
char *saves, *sptr, *stok, *sweep = NULL, *saven, *nptr, *ntok, *node = NULL;
|
2023-10-17 00:42:31 +02:00
|
|
|
int node_dataset = -1; /* dataset specified as %<n> after node/bus/expression name */
|
|
|
|
|
char *ntok_copy = NULL; /* copy of ntok without %<n> */
|
|
|
|
|
char *custom_rawfile = NULL; /* "rawfile" attr. set in graph: load and switch to specified raw */
|
|
|
|
|
char *sim_type = NULL;
|
|
|
|
|
Raw *raw = NULL;
|
2023-11-17 01:20:52 +01:00
|
|
|
char *tmp_ptr = NULL;
|
|
|
|
|
int save_extra_idx = -1;
|
2024-06-23 15:59:22 +02:00
|
|
|
int autoload = 0, save_datasets = -1, save_npoints = -1;
|
2024-09-06 16:57:13 +02:00
|
|
|
const char *ptr;
|
2023-10-11 14:05:27 +02:00
|
|
|
|
2025-04-14 12:31:59 +02:00
|
|
|
autoload = !strboolcmp(get_tok_value(r->prop_ptr,"autoload", 0), "true");
|
2024-06-23 15:59:22 +02:00
|
|
|
if(autoload == 0) autoload = 2;
|
2023-10-17 00:42:31 +02:00
|
|
|
dbg(1, "graph_fullyzoom(): graph_dataset=%d\n", graph_dataset);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node, get_tok_value(r->prop_ptr,"node", 0));
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &sweep, get_tok_value(r->prop_ptr,"sweep", 0));
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2024-11-25 14:29:19 +01:00
|
|
|
ptr = get_tok_value(r->prop_ptr,"rawfile", 0);
|
2024-09-06 16:57:13 +02:00
|
|
|
if(!ptr[0]) {
|
2025-04-18 02:02:15 +02:00
|
|
|
if(xctx->raw && xctx->raw->rawfile) my_strdup2(_ALLOC_ID_, &custom_rawfile, xctx->raw->rawfile);
|
|
|
|
|
else my_strdup2(_ALLOC_ID_, &custom_rawfile, "");
|
2024-09-06 16:57:13 +02:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &custom_rawfile, ptr);
|
2024-09-06 16:57:13 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &sim_type, get_tok_value(r->prop_ptr,"sim_type", 0));
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2023-11-17 01:20:52 +01:00
|
|
|
save_extra_idx = xctx->extra_idx;
|
2023-02-26 13:56:52 +01:00
|
|
|
nptr = node;
|
|
|
|
|
sptr = sweep;
|
|
|
|
|
start = (gr->gx1 <= gr->gx2) ? gr->gx1 : gr->gx2;
|
|
|
|
|
end = (gr->gx1 <= gr->gx2) ? gr->gx2 : gr->gx1;
|
|
|
|
|
|
2024-12-05 02:54:08 +01:00
|
|
|
while( (ntok = my_strtok_r(nptr, "\n", "\"", 4, &saven)) ) {
|
2023-11-17 01:20:52 +01:00
|
|
|
char *nd = NULL;
|
|
|
|
|
char str_extra_idx[30];
|
2023-11-07 01:05:45 +01:00
|
|
|
|
2023-11-17 01:20:52 +01:00
|
|
|
if(sch_waves_loaded() != -1 && custom_rawfile[0]) {
|
2024-06-23 15:59:22 +02:00
|
|
|
extra_rawfile(autoload, custom_rawfile, sim_type[0] ? sim_type : xctx->raw->sim_type, -1.0, -1.0);
|
2023-11-17 01:20:52 +01:00
|
|
|
}
|
|
|
|
|
raw = xctx->raw;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &nd, find_nth(ntok, "%", "\"", 0, 2));
|
2023-10-17 00:42:31 +02:00
|
|
|
/* if %<n> is specified after node name, <n> is the dataset number to plot in graph */
|
|
|
|
|
if(nd[0]) {
|
2023-11-17 01:20:52 +01:00
|
|
|
int pos = 1;
|
|
|
|
|
if(isonlydigit(find_nth(nd, "\n ", "\"", 0, 1))) pos = 2;
|
|
|
|
|
if(raw && raw->values) {
|
|
|
|
|
char *node_rawfile = NULL;
|
|
|
|
|
char *node_sim_type = NULL;
|
|
|
|
|
tclvareval("subst {", find_nth(nd, "\n ", "\"", 0, pos), "}", NULL);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node_rawfile, tclresult());
|
2023-11-17 01:20:52 +01:00
|
|
|
tclvareval("subst {", find_nth(nd, "\n ", "\"", 0, pos + 1), "}", NULL);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node_sim_type, tclresult()[0] ? tclresult() :
|
2023-11-17 01:20:52 +01:00
|
|
|
sim_type[0] ? sim_type : xctx->raw->sim_type);
|
|
|
|
|
dbg(1, "node_rawfile=|%s| node_sim_type=|%s|\n", node_rawfile, node_sim_type);
|
|
|
|
|
if(node_rawfile && node_rawfile[0]) {
|
2024-06-23 15:59:22 +02:00
|
|
|
extra_rawfile(autoload, node_rawfile, node_sim_type, -1.0, -1.0);
|
2023-11-17 01:20:52 +01:00
|
|
|
raw = xctx->raw;
|
|
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &node_rawfile);
|
|
|
|
|
my_free(_ALLOC_ID_, &node_sim_type);
|
2023-11-17 01:20:52 +01:00
|
|
|
}
|
|
|
|
|
if(pos == 2) node_dataset = atoi(nd);
|
|
|
|
|
else node_dataset = -1;
|
|
|
|
|
dbg(1, "nd=|%s|, node_dataset = %d\n", nd, node_dataset);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &ntok_copy, find_nth(ntok, "%", "\"", 4, 1));
|
2023-10-17 00:42:31 +02:00
|
|
|
} else {
|
|
|
|
|
node_dataset = -1;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &ntok_copy, ntok);
|
2023-10-17 00:42:31 +02:00
|
|
|
}
|
|
|
|
|
|
2023-11-17 01:20:52 +01:00
|
|
|
/* transform multiple OP points into a dc sweep */
|
|
|
|
|
if(raw && raw->sim_type && !strcmp(raw->sim_type, "op") && raw->datasets > 1 && raw->npoints[0] == 1) {
|
|
|
|
|
save_datasets = raw->datasets;
|
|
|
|
|
raw->datasets = 1;
|
|
|
|
|
save_npoints = raw->npoints[0];
|
|
|
|
|
raw->npoints[0] = raw->allpoints;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &nd);
|
2023-11-17 01:20:52 +01:00
|
|
|
dbg(1, "ntok=|%s|\nntok_copy=|%s|\nnode_dataset=%d\n", ntok, ntok_copy, node_dataset);
|
|
|
|
|
|
|
|
|
|
tmp_ptr = find_nth(ntok_copy, ";", "\"", 4, 2);
|
|
|
|
|
if(strstr(tmp_ptr, ",")) {
|
|
|
|
|
tmp_ptr = find_nth(tmp_ptr, ",", "\"", 4, 1);
|
|
|
|
|
/* also trim spaces */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &bus_msb, trim_chars(tmp_ptr, "\n "));
|
2023-11-17 01:20:52 +01:00
|
|
|
}
|
2025-01-04 11:33:58 +01:00
|
|
|
dbg(1, "ntok_copy=|%s|, bus_msb=|%s|\n", ntok_copy, bus_msb ? bus_msb : "<NULL>");
|
2023-10-11 14:05:27 +02:00
|
|
|
stok = my_strtok_r(sptr, "\n\t ", "\"", 0, &saves);
|
2023-02-26 13:56:52 +01:00
|
|
|
nptr = sptr = NULL;
|
|
|
|
|
if(stok && stok[0]) {
|
2024-02-25 12:21:13 +01:00
|
|
|
sweep_idx = get_raw_index(stok, NULL);
|
2023-02-26 13:56:52 +01:00
|
|
|
if( sweep_idx == -1) sweep_idx = 0;
|
2023-02-26 13:07:14 +01:00
|
|
|
}
|
2023-10-17 00:42:31 +02:00
|
|
|
dbg(1, "graph_fullyzoom(): ntok_copy=%s\n", ntok_copy);
|
2023-02-26 13:56:52 +01:00
|
|
|
v = -1;
|
|
|
|
|
if(!bus_msb) {
|
|
|
|
|
char *express = NULL;
|
2023-10-17 00:42:31 +02:00
|
|
|
if(strstr(ntok_copy, ";")) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &express, find_nth(ntok_copy, ";", "\"", 0, 2));
|
2023-02-26 13:56:52 +01:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &express, ntok_copy);
|
2023-02-26 13:56:52 +01:00
|
|
|
}
|
|
|
|
|
if(strpbrk(express, " \n\t")) {
|
2023-10-17 16:39:25 +02:00
|
|
|
/* we *need* to recalculate the expression column for any new expression
|
|
|
|
|
* This is *expecially needed if graph contains more than one expression */
|
|
|
|
|
v = calc_custom_data_yrange(sweep_idx, express, gr);
|
2023-02-26 13:56:52 +01:00
|
|
|
} else {
|
2024-02-25 12:21:13 +01:00
|
|
|
v = get_raw_index(express, NULL);
|
2023-02-26 13:56:52 +01:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &express);
|
2023-10-11 14:05:27 +02:00
|
|
|
dbg(1, "graph_fullyzoom(): v=%d\n", v);
|
2023-02-26 13:07:14 +01:00
|
|
|
}
|
2023-10-11 14:05:27 +02:00
|
|
|
if(xctx->raw && v >= 0) {
|
2023-10-17 00:42:31 +02:00
|
|
|
int dataset = node_dataset >=0 ? node_dataset : graph_dataset;
|
2023-10-11 14:05:27 +02:00
|
|
|
int sweepvar_wrap = 0; /* incremented on new dataset or sweep variable wrap */
|
2023-10-16 09:53:03 +02:00
|
|
|
int ofs = 0, ofs_end;
|
2023-10-11 14:05:27 +02:00
|
|
|
for(dset = 0 ; dset < raw->datasets; dset++) {
|
2023-10-16 11:16:12 +02:00
|
|
|
double xx, xx0 = 0.0; /* gcc gives false warnings if xx0 not initialized here */
|
2023-10-11 14:05:27 +02:00
|
|
|
int cnt=0, wrap;
|
|
|
|
|
register SPICE_DATA *gv = raw->values[sweep_idx];
|
2025-01-24 03:52:20 +01:00
|
|
|
register SPICE_DATA *gv0 = raw->values[0];
|
2023-10-16 09:53:03 +02:00
|
|
|
ofs_end = ofs + raw->npoints[dset];
|
2025-01-08 13:27:07 +01:00
|
|
|
|
|
|
|
|
/* optimization: skip unwanted datasets, if no dc no need to detect sweep variable wraps */
|
|
|
|
|
if(dataset >= 0 && strcmp(xctx->raw->sim_type, "dc") && dataset != sweepvar_wrap) goto done;
|
2023-10-16 09:53:03 +02:00
|
|
|
for(p = ofs ; p < ofs_end; p++) {
|
2023-10-11 14:05:27 +02:00
|
|
|
if(gr->logx) xx = mylog10(gv[p]);
|
|
|
|
|
else xx = gv[p];
|
2025-01-24 03:52:20 +01:00
|
|
|
if(p == ofs) xx0 = gv0[p];
|
2025-11-01 22:43:48 +01:00
|
|
|
wrap = xctx->raw->sim_type && !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0;
|
2023-10-11 14:05:27 +02:00
|
|
|
if(wrap) {
|
|
|
|
|
sweepvar_wrap++;
|
|
|
|
|
cnt = 0;
|
|
|
|
|
}
|
|
|
|
|
if(dataset == -1 || dataset == sweepvar_wrap) {
|
2025-01-27 15:33:46 +01:00
|
|
|
/* dbg(1, "graph_fullyzoom(): dataset=%d node=%s\n", dataset, raw->names[v]); */
|
2023-10-11 14:05:27 +02:00
|
|
|
if( xx >= start && xx <= end) {
|
|
|
|
|
if(gr->logy)
|
|
|
|
|
val =mylog10(raw->values[v][p]);
|
|
|
|
|
else
|
|
|
|
|
val = raw->values[v][p];
|
|
|
|
|
if(firstyval || val < min) min = val;
|
|
|
|
|
if(firstyval || val > max) max = val;
|
|
|
|
|
firstyval = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(xx >= start && xx <= end) {
|
|
|
|
|
++cnt;
|
|
|
|
|
}
|
|
|
|
|
} /* for(p = ofs ; p < ofs + raw->npoints[dset]; p++) */
|
2025-01-08 13:27:07 +01:00
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
|
2023-10-11 14:05:27 +02:00
|
|
|
/* offset pointing to next dataset */
|
2023-10-16 09:53:03 +02:00
|
|
|
ofs = ofs_end;
|
2023-10-11 14:05:27 +02:00
|
|
|
sweepvar_wrap++;
|
|
|
|
|
} /* for(dset...) */
|
2023-02-26 13:07:14 +01:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
if(bus_msb) my_free(_ALLOC_ID_, &bus_msb);
|
2023-11-17 01:20:52 +01:00
|
|
|
if(save_npoints != -1) { /* restore multiple OP points from artificial dc sweep */
|
|
|
|
|
raw->datasets = save_datasets;
|
|
|
|
|
raw->npoints[0] = save_npoints;
|
|
|
|
|
}
|
2024-02-23 04:19:29 +01:00
|
|
|
if(save_extra_idx != -1 && save_extra_idx != xctx->extra_idx) {
|
2023-11-17 01:20:52 +01:00
|
|
|
my_snprintf(str_extra_idx, S(str_extra_idx), "%d", save_extra_idx);
|
2023-12-31 20:33:08 +01:00
|
|
|
extra_rawfile(2, str_extra_idx, NULL, -1.0, -1.0);
|
2023-11-17 01:20:52 +01:00
|
|
|
raw = xctx->raw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} /* while( (ntok = my_strtok_r(nptr, "\n\t ", "\"", 0, &saven)) ) */
|
2023-02-26 13:56:52 +01:00
|
|
|
if(max == min) max += 0.01;
|
|
|
|
|
min = floor_to_n_digits(min, 2);
|
|
|
|
|
max = ceil_to_n_digits(max, 2);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &node);
|
|
|
|
|
my_free(_ALLOC_ID_, &sweep);
|
|
|
|
|
my_free(_ALLOC_ID_, &custom_rawfile);
|
|
|
|
|
my_free(_ALLOC_ID_, &sim_type);
|
|
|
|
|
if(ntok_copy) my_free(_ALLOC_ID_, &ntok_copy);
|
|
|
|
|
my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(min)));
|
|
|
|
|
my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(max)));
|
2023-02-26 13:56:52 +01:00
|
|
|
need_redraw = 1;
|
|
|
|
|
} else { /* digital plot */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos1",
|
2023-02-26 13:56:52 +01:00
|
|
|
get_tok_value(r->prop_ptr, "y1", 0) ));
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos2",
|
2023-02-26 13:56:52 +01:00
|
|
|
get_tok_value(r->prop_ptr, "y2", 0) ));
|
|
|
|
|
need_redraw = 1;
|
|
|
|
|
}
|
|
|
|
|
return need_redraw;
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
2023-02-26 13:07:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-29 05:11:39 +01:00
|
|
|
/* draw bussed signals: ntok is a comma separated list of items, first item is bus name,
|
|
|
|
|
* following are bits that are bundled together:
|
|
|
|
|
LDA,LDA[3],LDA[2],LDA1],LDA[0]
|
|
|
|
|
*/
|
2024-03-30 19:20:51 +01:00
|
|
|
static void draw_graph_bus_points(const char *ntok, int n_bits, SPICE_DATA **idx_arr,
|
2022-09-25 21:11:52 +02:00
|
|
|
int first, int last, int wave_col, int sweep_idx, int wcnt, int n_nodes, Graph_ctx *gr, void *ct)
|
2021-12-29 05:11:39 +01:00
|
|
|
{
|
2022-01-06 03:49:18 +01:00
|
|
|
int p;
|
2022-01-04 05:54:25 +01:00
|
|
|
double s1 = DIG_NWAVES; /* 1/DIG_NWAVES waveforms fit in graph if unscaled vertically */
|
|
|
|
|
double s2 = DIG_SPACE; /* (DIG_NWAVES - DIG_SPACE) spacing between traces */
|
2022-01-05 23:07:08 +01:00
|
|
|
double c = (n_nodes - wcnt) * s1 * gr->gh - gr->gy1 * s2; /* trace baseline */
|
|
|
|
|
double c1 = c + gr->gh * 0.5 * s2; /* trace y-center, used for clipping */
|
2022-08-05 00:57:03 +02:00
|
|
|
double lx1;
|
|
|
|
|
double lx2;
|
2022-01-05 23:07:08 +01:00
|
|
|
double ylow = DW_Y(c + gr->gy2 * s2); /* swapped as xschem Y coordinates are top-bottom */
|
|
|
|
|
double yhigh = DW_Y(c + gr->gy1 * s2);
|
2022-01-04 05:54:25 +01:00
|
|
|
char busval[1024], old_busval[1024];
|
2022-01-05 17:38:01 +01:00
|
|
|
double xval=0.0, xval_old=0.0;
|
2021-12-29 05:11:39 +01:00
|
|
|
double ydelta = fabs(yhigh - ylow);
|
|
|
|
|
double labsize = 0.015 * ydelta;
|
|
|
|
|
double charwidth = labsize * 38.0;
|
2021-12-29 06:32:02 +01:00
|
|
|
double x_size = 1.5 * xctx->zoom;
|
2022-01-07 01:54:41 +01:00
|
|
|
double vthh = gr->gy1 * 0.2 + gr->gy2 * 0.8;
|
|
|
|
|
double vthl = gr->gy1 * 0.8 + gr->gy2 * 0.2;
|
2022-01-06 01:10:06 +01:00
|
|
|
int hex_digits = ((n_bits - 1) >> 2) + 1;
|
2023-10-11 14:05:27 +02:00
|
|
|
Raw *raw = xctx->raw;
|
2022-08-05 00:57:03 +02:00
|
|
|
|
2023-10-11 14:05:27 +02:00
|
|
|
if(!raw) {
|
|
|
|
|
dbg(0, "draw_graph_bus_points(): no raw struct allocated\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-27 20:55:17 +02:00
|
|
|
for(p=0;p<cadlayers; ++p) {
|
|
|
|
|
XSetLineAttributes(display, xctx->gc[p],
|
2024-12-19 23:53:11 +01:00
|
|
|
XLINEWIDTH(gr->linewidth_mult * xctx->lw), LineSolid, LINECAP, LINEJOIN);
|
2023-04-27 20:55:17 +02:00
|
|
|
}
|
2022-08-05 00:57:03 +02:00
|
|
|
if(gr->logx) {
|
2023-10-11 14:05:27 +02:00
|
|
|
lx1 = W_X(mylog10(raw->values[sweep_idx][first]));
|
|
|
|
|
lx2 = W_X(mylog10(raw->values[sweep_idx][last]));
|
2022-08-05 00:57:03 +02:00
|
|
|
} else {
|
2023-10-11 14:05:27 +02:00
|
|
|
lx1 = W_X(raw->values[sweep_idx][first]);
|
|
|
|
|
lx2 = W_X(raw->values[sweep_idx][last]);
|
2022-08-05 00:57:03 +02:00
|
|
|
}
|
2022-01-05 23:07:08 +01:00
|
|
|
if(c1 >= gr->ypos1 && c1 <=gr->ypos2) {
|
2022-02-07 16:21:06 +01:00
|
|
|
set_thick_waves(1, wcnt, wave_col, gr);
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(wave_col, NOW, lx1, ylow, lx2, ylow, 0.0, 0, ct);
|
|
|
|
|
drawline(wave_col, NOW, lx1, yhigh, lx2, yhigh, 0.0, 0, ct);
|
2022-01-04 06:21:50 +01:00
|
|
|
for(p = first ; p <= last; p++) {
|
|
|
|
|
/* calculate value of bus by adding all binary bits */
|
|
|
|
|
/* hex_digits = */
|
2022-01-07 01:54:41 +01:00
|
|
|
get_bus_value(n_bits, hex_digits, idx_arr, p, busval, vthl, vthh);
|
2022-08-05 00:57:03 +02:00
|
|
|
if(gr->logx) {
|
2023-10-11 14:05:27 +02:00
|
|
|
xval = W_X(mylog10(raw->values[sweep_idx][p]));
|
2022-08-05 00:57:03 +02:00
|
|
|
} else {
|
2023-10-11 14:05:27 +02:00
|
|
|
xval = W_X(raw->values[sweep_idx][p]);
|
2022-08-05 00:57:03 +02:00
|
|
|
}
|
2022-01-04 06:21:50 +01:00
|
|
|
/* used to draw bus value before 1st transition */
|
|
|
|
|
if(p == first) {
|
2022-01-06 03:49:18 +01:00
|
|
|
my_strncpy(old_busval, busval, hex_digits+1);
|
2022-01-04 06:21:50 +01:00
|
|
|
xval_old = xval;
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2022-01-04 06:21:50 +01:00
|
|
|
if(p > first && strcmp(busval, old_busval)) {
|
|
|
|
|
/* draw transition ('X') */
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(BACKLAYER, NOW, xval-x_size, yhigh, xval+x_size, yhigh, 0.0, 0, ct);
|
|
|
|
|
drawline(BACKLAYER, NOW, xval-x_size, ylow, xval+x_size, ylow, 0.0, 0, ct);
|
|
|
|
|
drawline(wave_col, NOW, xval-x_size, ylow, xval+x_size, yhigh, 0.0, 0, ct);
|
|
|
|
|
drawline(wave_col, NOW, xval-x_size, yhigh, xval+x_size, ylow, 0.0, 0, ct);
|
2022-01-04 06:21:50 +01:00
|
|
|
/* draw hex bus value if there is enough room */
|
2022-01-06 03:49:18 +01:00
|
|
|
if( fabs(xval - xval_old) > hex_digits * charwidth) {
|
2022-01-04 06:21:50 +01:00
|
|
|
draw_string(wave_col, NOW, old_busval, 2, 0, 1, 0, (xval + xval_old) * 0.5,
|
|
|
|
|
yhigh, labsize, labsize);
|
|
|
|
|
}
|
2022-01-06 03:49:18 +01:00
|
|
|
my_strncpy(old_busval, busval, hex_digits + 1);
|
2022-01-04 06:21:50 +01:00
|
|
|
xval_old = xval;
|
|
|
|
|
} /* if(p > first && busval != old_busval) */
|
|
|
|
|
} /* for(p = first ; p < last; p++) */
|
|
|
|
|
/* draw hex bus value after last transition */
|
2022-01-06 03:49:18 +01:00
|
|
|
if( fabs(xval - xval_old) > hex_digits * charwidth) {
|
2022-01-04 06:21:50 +01:00
|
|
|
draw_string(wave_col, NOW, old_busval, 2, 0, 1, 0, (xval + xval_old) * 0.5,
|
|
|
|
|
yhigh, labsize, labsize);
|
|
|
|
|
}
|
2022-02-07 16:21:06 +01:00
|
|
|
set_thick_waves(0, wcnt, wave_col, gr);
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2023-04-27 20:55:17 +02:00
|
|
|
for(p=0;p<cadlayers; ++p) {
|
2023-09-23 01:46:39 +02:00
|
|
|
XSetLineAttributes(display, xctx->gc[p], XLINEWIDTH(xctx->lw), LineSolid, LINECAP , LINEJOIN);
|
2023-04-27 20:55:17 +02:00
|
|
|
}
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2024-11-27 16:13:48 +01:00
|
|
|
|
2023-04-14 15:41:00 +02:00
|
|
|
#define MAX_POLY_POINTS 4096*16
|
2022-02-12 04:55:02 +01:00
|
|
|
/* wcnt is the nth wave in graph, idx is the index in spice raw file */
|
|
|
|
|
static void draw_graph_points(int idx, int first, int last,
|
2022-09-25 21:11:52 +02:00
|
|
|
XPoint *point, int wave_col, int wcnt, int n_nodes, Graph_ctx *gr, void *ct)
|
2021-12-27 18:42:56 +01:00
|
|
|
{
|
2024-11-21 10:58:15 +01:00
|
|
|
int p, x;
|
2022-01-06 03:49:18 +01:00
|
|
|
register double yy;
|
|
|
|
|
register int digital;
|
2021-12-27 18:42:56 +01:00
|
|
|
int poly_npoints = 0;
|
2022-01-03 02:28:34 +01:00
|
|
|
double s1;
|
|
|
|
|
double s2;
|
2024-11-21 10:58:15 +01:00
|
|
|
double c = 0 /* , c1 */;
|
2023-10-11 14:05:27 +02:00
|
|
|
Raw *raw = xctx->raw;
|
|
|
|
|
register SPICE_DATA *gv;
|
|
|
|
|
|
|
|
|
|
if(!raw) {
|
|
|
|
|
dbg(0, "draw_graph_points(): no raw struct allocated\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
gv = raw->values[idx];
|
2022-01-02 22:24:44 +01:00
|
|
|
|
2022-02-12 04:55:02 +01:00
|
|
|
dbg(1, "draw_graph_points: idx=%d, first=%d, last=%d, wcnt=%d\n", idx, first, last, wcnt);
|
2023-10-13 18:46:15 +02:00
|
|
|
if(idx == -1) return;
|
2023-04-27 20:55:17 +02:00
|
|
|
for(p=0;p<cadlayers; ++p) {
|
2024-12-19 23:53:11 +01:00
|
|
|
if(gr->mode == 1 || gr->mode == 2) { /* Histograms */
|
|
|
|
|
XSetLineAttributes(display, xctx->gc[p],
|
|
|
|
|
XLINEWIDTH(gr->linewidth_mult * xctx->lw), LineSolid, xCap , xJoin);
|
|
|
|
|
} else {
|
|
|
|
|
XSetLineAttributes(display, xctx->gc[p],
|
|
|
|
|
XLINEWIDTH(gr->linewidth_mult * xctx->lw), LineSolid, LINECAP , LINEJOIN);
|
|
|
|
|
}
|
2023-04-27 20:55:17 +02:00
|
|
|
}
|
2022-01-06 03:49:18 +01:00
|
|
|
digital = gr->digital;
|
|
|
|
|
if(digital) {
|
2022-01-04 05:54:25 +01:00
|
|
|
s1 = DIG_NWAVES; /* 1/DIG_NWAVES waveforms fit in graph if unscaled vertically */
|
|
|
|
|
s2 = DIG_SPACE; /* (DIG_NWAVES - DIG_SPACE) spacing between traces */
|
2022-01-05 23:07:08 +01:00
|
|
|
c = (n_nodes - wcnt) * s1 * gr->gh - gr->gy1 * s2; /* trace baseline */
|
2024-11-21 10:58:15 +01:00
|
|
|
/* c1 = c + gr->gh * 0.5 * s2; */ /* trace y-center, used for clipping */
|
|
|
|
|
}
|
|
|
|
|
/* below condition seems no more necessary as a clip is set via bbox(SET...) */
|
|
|
|
|
/* if( 1 || !digital || (c1 >= gr->ypos1 && c1 <= gr->ypos2) ) { */
|
|
|
|
|
for(p = first ; p <= last; p++) {
|
|
|
|
|
yy = gv[p];
|
2024-12-07 11:40:49 +01:00
|
|
|
|
|
|
|
|
/* Below CLIP calls (for digital and non digital graphs) for Windows
|
|
|
|
|
* clamp y-value of waves to be inside graph area. Not a clean solution
|
2024-12-07 00:56:04 +01:00
|
|
|
* but avoids drawing outside of graph area when moving vertically on Windows
|
|
|
|
|
* platform where is no XSetClipRectangles()
|
|
|
|
|
* waveform points outise graph are drawn as a line on top or bottom of graph
|
|
|
|
|
* <<<<< FIXME: remove these points completely
|
|
|
|
|
*/
|
2024-11-21 10:58:15 +01:00
|
|
|
if(digital) {
|
|
|
|
|
yy = c + yy *s2;
|
2024-12-07 11:40:49 +01:00
|
|
|
#if !defined(__unix__)
|
|
|
|
|
yy = CLIP(DS_Y(yy), Y_TO_SCREEN(gr->y1), Y_TO_SCREEN(gr->y2));
|
|
|
|
|
#else
|
|
|
|
|
yy = CLIP(DS_Y(yy), -30000, 30000); /* only clip to 16 bit signed short limits */
|
|
|
|
|
#endif
|
2024-11-21 10:58:15 +01:00
|
|
|
/* Build poly y array. Translate from graph coordinates to screen coordinates */
|
2024-12-07 11:40:49 +01:00
|
|
|
point[poly_npoints].y = (short)yy;
|
2024-11-21 10:58:15 +01:00
|
|
|
} else {
|
|
|
|
|
/* Build poly y array. Translate from graph coordinates to screen coordinates */
|
|
|
|
|
if(gr->logy) yy = mylog10(yy);
|
2024-12-07 11:40:49 +01:00
|
|
|
#if !defined(__unix__)
|
|
|
|
|
yy = CLIP(S_Y(yy), Y_TO_SCREEN(gr->y1), Y_TO_SCREEN(gr->y2));
|
|
|
|
|
#else
|
|
|
|
|
yy = CLIP(S_Y(yy), -30000, 30000); /* only clip to 16 bit signed short limits */
|
|
|
|
|
#endif
|
|
|
|
|
point[poly_npoints].y = (short)yy;
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2024-11-21 10:58:15 +01:00
|
|
|
poly_npoints++;
|
|
|
|
|
}
|
|
|
|
|
set_thick_waves(1, wcnt, wave_col, gr);
|
2024-12-09 17:25:40 +01:00
|
|
|
if(digital || gr->mode == 0) { /* Line */
|
|
|
|
|
for(x = 0; x < 2; x++) {
|
|
|
|
|
Drawable w;
|
|
|
|
|
int offset = 0, size;
|
|
|
|
|
XPoint *pt = point;
|
|
|
|
|
if(x == 0 && xctx->draw_window) w = xctx->window;
|
|
|
|
|
else if(x == 1 && xctx->draw_pixmap) w = xctx->save_pixmap;
|
|
|
|
|
else continue;
|
|
|
|
|
while(1) {
|
|
|
|
|
pt = point + offset;
|
|
|
|
|
size = poly_npoints - offset;
|
|
|
|
|
if(size > MAX_POLY_POINTS) size = MAX_POLY_POINTS;
|
|
|
|
|
/* dbg(0, "draw_graph_points(): drawing from %d, size %d\n", offset, size);*/
|
|
|
|
|
XDrawLines(display, w, xctx->gc[wave_col], pt, size, CoordModeOrigin);
|
|
|
|
|
if(offset + size >= poly_npoints) break;
|
|
|
|
|
offset += MAX_POLY_POINTS -1; /* repeat last point on next iteration */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(gr->mode == 1) { /* HistoV */
|
2024-12-10 00:07:33 +01:00
|
|
|
int y2 = (int)S_Y(0.0);
|
2024-12-09 17:25:40 +01:00
|
|
|
for(x = 0; x < 2; x++) {
|
|
|
|
|
Drawable w;
|
|
|
|
|
if(x == 0 && xctx->draw_window) w = xctx->window;
|
|
|
|
|
else if(x == 1 && xctx->draw_pixmap) w = xctx->save_pixmap;
|
|
|
|
|
else continue;
|
|
|
|
|
for(p = 0; p < poly_npoints; p++) {
|
2024-12-10 00:07:33 +01:00
|
|
|
if(point[p].y < y2) {
|
2024-12-09 17:25:40 +01:00
|
|
|
XDrawLine(display, w, xctx->gc[wave_col], point[p].x, point[p].y, point[p].x, y2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if(gr->mode == 2) { /* HistoH */
|
2024-12-10 00:07:33 +01:00
|
|
|
int x1 = (int)S_X(0.0);
|
2024-12-09 17:25:40 +01:00
|
|
|
for(x = 0; x < 2; x++) {
|
|
|
|
|
Drawable w;
|
|
|
|
|
if(x == 0 && xctx->draw_window) w = xctx->window;
|
|
|
|
|
else if(x == 1 && xctx->draw_pixmap) w = xctx->save_pixmap;
|
|
|
|
|
else continue;
|
|
|
|
|
for(p = 0; p < poly_npoints; p++) {
|
2024-12-10 00:07:33 +01:00
|
|
|
if(point[p].x > x1) {
|
|
|
|
|
XDrawLine(display, w, xctx->gc[wave_col], x1, point[p].y, point[p].x, point[p].y);
|
2024-12-09 17:25:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-27 20:47:27 +01:00
|
|
|
}
|
2024-11-21 10:58:15 +01:00
|
|
|
}
|
2024-12-09 17:25:40 +01:00
|
|
|
|
2024-11-21 10:58:15 +01:00
|
|
|
set_thick_waves(0, wcnt, wave_col, gr);
|
|
|
|
|
/* } else dbg(1, "skipping wave: %s\n", raw->names[idx]); */
|
2023-04-27 20:55:17 +02:00
|
|
|
for(p=0;p<cadlayers; ++p) {
|
2023-09-23 01:46:39 +02:00
|
|
|
XSetLineAttributes(display, xctx->gc[p], XLINEWIDTH(xctx->lw), LineSolid, LINECAP , LINEJOIN);
|
2023-04-27 20:55:17 +02:00
|
|
|
}
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-25 21:11:52 +02:00
|
|
|
static void draw_graph_grid(Graph_ctx *gr, void *ct)
|
2021-12-29 05:11:39 +01:00
|
|
|
{
|
2022-12-02 00:13:54 +01:00
|
|
|
double deltax, startx, deltay, starty, wx,wy, dash_size;
|
2021-12-29 05:11:39 +01:00
|
|
|
int j, k;
|
2022-01-05 23:07:08 +01:00
|
|
|
double mark_size = gr->marginy/10.0;
|
2021-12-29 05:11:39 +01:00
|
|
|
|
|
|
|
|
/* calculate dash length for grid lines */
|
2022-12-02 00:13:54 +01:00
|
|
|
dash_size = 1.5 * xctx->mooz;
|
|
|
|
|
dash_size = dash_size < 1.0 ? 0.0: (dash_size > 3.0 ? 3.0 : 2.0);
|
2021-12-29 05:11:39 +01:00
|
|
|
|
2021-12-30 15:45:38 +01:00
|
|
|
/* clipping everything outside container area */
|
2021-12-29 05:11:39 +01:00
|
|
|
/* background */
|
2025-02-18 02:25:55 +01:00
|
|
|
filledrect(0, NOW, gr->rx1, gr->ry1, gr->rx2, gr->ry2, 2, -1, -1);
|
2021-12-29 05:11:39 +01:00
|
|
|
/* graph bounding box */
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(GRIDLAYER, NOW, gr->rx1, gr->ry1, gr->rx2, gr->ry2, 0.0, 2, -1, -1);
|
2021-12-29 05:11:39 +01:00
|
|
|
|
2021-12-30 15:45:38 +01:00
|
|
|
bbox(START, 0.0, 0.0, 0.0, 0.0);
|
2022-01-05 23:07:08 +01:00
|
|
|
bbox(ADD, gr->rx1, gr->ry1, gr->rx2, gr->ry2);
|
2021-12-30 15:45:38 +01:00
|
|
|
bbox(SET_INSIDE, 0.0, 0.0, 0.0, 0.0);
|
2021-12-29 05:11:39 +01:00
|
|
|
/* vertical grid lines */
|
2022-08-05 00:57:03 +02:00
|
|
|
deltax = axis_increment(gr->gx1, gr->gx2, gr->divx, (gr->logx));
|
2022-01-05 23:07:08 +01:00
|
|
|
startx = axis_start(gr->gx1, deltax, gr->divx);
|
2023-02-18 09:44:11 +01:00
|
|
|
for(j = -1;; ++j) { /* start one interval before to allow sub grids at beginning */
|
2021-12-29 05:11:39 +01:00
|
|
|
wx = startx + j * deltax;
|
2023-02-18 09:44:11 +01:00
|
|
|
if(gr->subdivx > 0) for(k = 1; k <=gr->subdivx; ++k) {
|
2022-02-02 18:33:16 +01:00
|
|
|
double subwx;
|
2022-08-08 01:18:42 +02:00
|
|
|
if(gr->logx)
|
|
|
|
|
subwx = wx + deltax * mylog10(1.0 + (double)k * 9.0 / ((double)gr->subdivx + 1.0));
|
|
|
|
|
else
|
2022-02-02 18:33:16 +01:00
|
|
|
subwx = wx + deltax * (double)k / ((double)gr->subdivx + 1.0);
|
2022-10-13 17:36:42 +02:00
|
|
|
if(!axis_within_range(subwx, gr->gx1, gr->gx2, deltax, gr->subdivx)) continue;
|
2022-01-05 23:07:08 +01:00
|
|
|
if(axis_end(subwx, deltax, gr->gx2)) break;
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(subwx), W_Y(gr->gy2), W_X(subwx), W_Y(gr->gy1), 0.0, (int)dash_size, ct);
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2022-10-13 17:36:42 +02:00
|
|
|
if(!axis_within_range(wx, gr->gx1, gr->gx2, deltax, gr->subdivx)) continue;
|
2022-01-05 23:07:08 +01:00
|
|
|
if(axis_end(wx, deltax, gr->gx2)) break;
|
|
|
|
|
/* swap order of gy1 and gy2 since grap y orientation is opposite to xorg orientation */
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(wx), W_Y(gr->gy2), W_X(wx), W_Y(gr->gy1), 0.0, (int)dash_size, ct);
|
|
|
|
|
drawline(GRIDLAYER, ADD, W_X(wx), W_Y(gr->gy1), W_X(wx), W_Y(gr->gy1) + mark_size, 0.0, 0, ct); /* axis marks */
|
2021-12-29 05:11:39 +01:00
|
|
|
/* X-axis labels */
|
2022-08-05 00:57:03 +02:00
|
|
|
if(gr->logx)
|
2022-09-28 19:14:31 +02:00
|
|
|
draw_string(3, NOW, dtoa_eng(pow(10, wx) * gr->unitx), 0, 0, 1, 0, W_X(wx),
|
|
|
|
|
gr->y2 + mark_size + 5 * gr->txtsizex, gr->txtsizex, gr->txtsizex);
|
2022-02-02 18:33:16 +01:00
|
|
|
else
|
2022-09-28 19:14:31 +02:00
|
|
|
draw_string(3, NOW, dtoa_eng(wx * gr->unitx), 0, 0, 1, 0, W_X(wx), gr->y2 + mark_size + 5 * gr->txtsizex,
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->txtsizex, gr->txtsizex);
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
|
|
|
|
/* first and last vertical box delimiters */
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx1), W_Y(gr->gy2), W_X(gr->gx1), W_Y(gr->gy1), 0.0, 0, ct);
|
|
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx2), W_Y(gr->gy2), W_X(gr->gx2), W_Y(gr->gy1), 0.0, 0, ct);
|
2021-12-29 05:11:39 +01:00
|
|
|
/* horizontal grid lines */
|
2022-01-05 23:07:08 +01:00
|
|
|
if(!gr->digital) {
|
2022-08-08 01:18:42 +02:00
|
|
|
deltay = axis_increment(gr->gy1, gr->gy2, gr->divy, gr->logy);
|
2022-01-05 23:07:08 +01:00
|
|
|
starty = axis_start(gr->gy1, deltay, gr->divy);
|
2024-11-07 00:39:53 +01:00
|
|
|
/* start one interval before to allow sub grids at beginning */
|
|
|
|
|
for(j = -1; gr->gy1 == gr->gy1 && gr->gy2 == gr->gy2; ++j) { /* gy1 and gy2 are not NaN */
|
2021-12-29 05:11:39 +01:00
|
|
|
wy = starty + j * deltay;
|
2023-02-18 09:44:11 +01:00
|
|
|
if(gr->subdivy > 0) for(k = 1; k <=gr->subdivy; ++k) {
|
2022-08-08 01:18:42 +02:00
|
|
|
double subwy;
|
|
|
|
|
if(gr->logy)
|
|
|
|
|
subwy = wy + deltay * mylog10(1.0 + (double)k * 9.0 / ((double)gr->subdivy + 1.0));
|
|
|
|
|
else
|
|
|
|
|
subwy = wy + deltay * (double)k / ((double)gr->subdivy + 1.0);
|
2022-10-13 17:36:42 +02:00
|
|
|
if(!axis_within_range(subwy, gr->gy1, gr->gy2, deltay, gr->subdivy)) continue;
|
2022-01-05 23:07:08 +01:00
|
|
|
if(axis_end(subwy, deltay, gr->gy2)) break;
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx1), W_Y(subwy), W_X(gr->gx2), W_Y(subwy), 0.0, (int)dash_size, ct);
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2022-10-13 17:36:42 +02:00
|
|
|
if(!axis_within_range(wy, gr->gy1, gr->gy2, deltay, gr->subdivy)) continue;
|
2022-01-05 23:07:08 +01:00
|
|
|
if(axis_end(wy, deltay, gr->gy2)) break;
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx1), W_Y(wy), W_X(gr->gx2), W_Y(wy), 0.0, (int)dash_size, ct);
|
|
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx1) - mark_size, W_Y(wy), W_X(gr->gx1), W_Y(wy), 0.0, 0, ct); /* axis marks */
|
2021-12-29 05:11:39 +01:00
|
|
|
/* Y-axis labels */
|
2022-08-08 01:18:42 +02:00
|
|
|
if(gr->logy)
|
2022-09-28 19:14:31 +02:00
|
|
|
draw_string(3, NOW, dtoa_eng(pow(10, wy) * gr->unity), 0, 1, 0, 1,
|
|
|
|
|
gr->x1 - mark_size - 5 * gr->txtsizey, W_Y(wy), gr->txtsizey, gr->txtsizey);
|
2022-08-08 01:18:42 +02:00
|
|
|
else
|
2022-09-28 19:14:31 +02:00
|
|
|
draw_string(3, NOW, dtoa_eng(wy * gr->unity), 0, 1, 0, 1, gr->x1 - mark_size - 5 * gr->txtsizey, W_Y(wy),
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->txtsizey, gr->txtsizey);
|
2021-12-27 18:42:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-12-29 05:11:39 +01:00
|
|
|
/* first and last horizontal box delimiters */
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx1), W_Y(gr->gy1), W_X(gr->gx2), W_Y(gr->gy1), 0.0, 0, ct);
|
|
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx1), W_Y(gr->gy2), W_X(gr->gx2), W_Y(gr->gy2), 0.0, 0, ct);
|
2021-12-29 05:11:39 +01:00
|
|
|
/* Horizontal axis (if in viewport) */
|
2022-01-05 23:07:08 +01:00
|
|
|
if(!gr->digital && gr->gy1 <= 0 && gr->gy2 >= 0)
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(gr->gx1), W_Y(0), W_X(gr->gx2), W_Y(0), 0.0, 0, ct);
|
2021-12-29 05:11:39 +01:00
|
|
|
/* Vertical axis (if in viewport)
|
2022-01-05 23:07:08 +01:00
|
|
|
* swap order of gy1 and gy2 since grap y orientation is opposite to xorg orientation */
|
|
|
|
|
if(gr->gx1 <= 0 && gr->gx2 >= 0)
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(GRIDLAYER, ADD, W_X(0), W_Y(gr->gy2), W_X(0), W_Y(gr->gy1), 0.0, 0, ct);
|
|
|
|
|
drawline(GRIDLAYER, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, ct);
|
2021-12-30 15:45:38 +01:00
|
|
|
bbox(END, 0.0, 0.0, 0.0, 0.0);
|
2021-12-27 18:42:56 +01:00
|
|
|
}
|
|
|
|
|
|
2023-02-26 11:22:19 +01:00
|
|
|
void setup_graph_data(int i, int skip, Graph_ctx *gr)
|
2021-12-22 04:39:23 +01:00
|
|
|
{
|
2022-01-05 23:07:08 +01:00
|
|
|
double tmp;
|
2021-12-22 04:39:23 +01:00
|
|
|
const char *val;
|
2022-01-05 23:07:08 +01:00
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
|
|
|
|
|
2022-09-28 19:14:31 +02:00
|
|
|
dbg(1, "setup_graph_data: i=%d\n", i);
|
2022-01-05 23:07:08 +01:00
|
|
|
/* default values */
|
2023-05-20 23:21:43 +02:00
|
|
|
gr->magx = gr->magy = 1.0;
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->divx = gr->divy = 5;
|
|
|
|
|
gr->subdivx = gr->subdivy = 0;
|
2022-08-05 00:57:03 +02:00
|
|
|
gr->logx = gr->logy = 0;
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->digital = 0;
|
2022-10-13 01:00:55 +02:00
|
|
|
gr->rainbow = 0;
|
2023-04-28 10:45:56 +02:00
|
|
|
gr->linewidth_mult = tclgetdoublevar("graph_linewidth_mult");
|
2025-01-07 04:20:32 +01:00
|
|
|
xctx->graph_flags &= ~(128 | 256); /* clear hcursor flags */
|
|
|
|
|
gr->hcursor1_y = gr->hcursor2_y = 0.0;
|
|
|
|
|
val = get_tok_value(r->prop_ptr,"hcursor1_y", 0);
|
|
|
|
|
if(val[0]) {
|
2025-01-10 01:52:54 +01:00
|
|
|
gr->hcursor1_y = atof_eng(val);
|
2025-01-07 04:20:32 +01:00
|
|
|
xctx->graph_flags |= 128;
|
|
|
|
|
}
|
|
|
|
|
val = get_tok_value(r->prop_ptr,"hcursor2_y", 0);
|
|
|
|
|
if(val[0]) {
|
2025-01-10 01:52:54 +01:00
|
|
|
gr->hcursor2_y = atof_eng(val);
|
2025-01-07 04:20:32 +01:00
|
|
|
xctx->graph_flags |= 256;
|
|
|
|
|
}
|
2022-01-06 07:37:44 +01:00
|
|
|
if(!skip) {
|
|
|
|
|
gr->gx1 = 0;
|
|
|
|
|
gr->gx2 = 1e-6;
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"x1", 0);
|
2025-01-10 01:52:54 +01:00
|
|
|
if(val[0]) gr->gx1 = atof_eng(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"x2", 0);
|
2025-01-10 01:52:54 +01:00
|
|
|
if(val[0]) gr->gx2 = atof_eng(val);
|
2022-01-06 07:37:44 +01:00
|
|
|
if(gr->gx1 == gr->gx2) gr->gx2 += 1e-6;
|
|
|
|
|
gr->gw = gr->gx2 - gr->gx1;
|
|
|
|
|
}
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->gy1 = 0;
|
|
|
|
|
gr->gy2 = 5;
|
|
|
|
|
gr->dataset = -1; /* -1 means 'plot all datasets' */
|
|
|
|
|
gr->ypos1 = 0;
|
|
|
|
|
gr->ypos2 = 2;
|
|
|
|
|
gr->digtxtsizelab = 0.3;
|
|
|
|
|
gr->txtsizelab = 0.3;
|
|
|
|
|
gr->txtsizex = 0.3;
|
|
|
|
|
gr->txtsizey = 0.3;
|
2021-12-30 15:45:38 +01:00
|
|
|
|
2021-12-22 18:25:15 +01:00
|
|
|
/* container (embedding rectangle) coordinates */
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->rx1 = r->x1;
|
|
|
|
|
gr->ry1 = r->y1;
|
|
|
|
|
gr->rx2 = r->x2;
|
|
|
|
|
gr->ry2 = r->y2;
|
|
|
|
|
|
|
|
|
|
/* screen position */
|
|
|
|
|
gr->sx1=X_TO_SCREEN(gr->rx1);
|
|
|
|
|
gr->sy1=Y_TO_SCREEN(gr->ry1);
|
|
|
|
|
gr->sx2=X_TO_SCREEN(gr->rx2);
|
|
|
|
|
gr->sy2=Y_TO_SCREEN(gr->ry2);
|
|
|
|
|
|
|
|
|
|
if(RECT_OUTSIDE(gr->sx1, gr->sy1, gr->sx2, gr->sy2,
|
|
|
|
|
xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) return;
|
|
|
|
|
|
|
|
|
|
gr->rw = gr->rx2 - gr->rx1;
|
|
|
|
|
gr->rh = gr->ry2 - gr->ry1;
|
|
|
|
|
|
2022-08-27 12:56:33 +02:00
|
|
|
/* wave to display in bold, -1=none */
|
2024-11-25 14:29:19 +01:00
|
|
|
val=get_tok_value(r->prop_ptr,"hilight_wave", 0);
|
2022-08-27 12:56:33 +02:00
|
|
|
if(val[0]) gr->hilight_wave = atoi(val);
|
|
|
|
|
else gr->hilight_wave = -1;
|
|
|
|
|
|
2024-11-25 14:29:19 +01:00
|
|
|
/* legend */
|
|
|
|
|
gr->legend = 1;
|
|
|
|
|
val = get_tok_value(r->prop_ptr,"legend", 0);
|
|
|
|
|
if(val[0]) gr->legend = atoi(val);
|
|
|
|
|
|
2024-12-09 17:25:40 +01:00
|
|
|
/* draw mode (0: Line, 1: Histo. Default: Line) */
|
|
|
|
|
val = get_tok_value(r->prop_ptr,"mode", 0);
|
|
|
|
|
if(!strcmp(val, "HistoV")) gr->mode = 1;
|
|
|
|
|
else if(!strcmp(val, "HistoH")) gr->mode = 2;
|
|
|
|
|
else gr->mode = 0;
|
|
|
|
|
|
2022-01-05 23:07:08 +01:00
|
|
|
/* get x/y range, grid info etc */
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"unitx", 0);
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->unitx_suffix = val[0];
|
|
|
|
|
gr->unitx = get_unit(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"unity", 0);
|
2022-08-05 00:57:03 +02:00
|
|
|
if(gr->logx) { /* AC */
|
2022-02-02 18:33:16 +01:00
|
|
|
gr->unity_suffix = '1';
|
|
|
|
|
gr->unity = 1.0;
|
|
|
|
|
} else {
|
|
|
|
|
gr->unity_suffix = val[0];
|
|
|
|
|
gr->unity = get_unit(val);
|
|
|
|
|
}
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"xlabmag", 0);
|
2023-05-20 23:21:43 +02:00
|
|
|
if(val[0]) gr->magx = atof(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"ylabmag", 0);
|
2023-05-20 23:21:43 +02:00
|
|
|
if(val[0]) gr->magy = atof(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"subdivx", 0);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(val[0]) gr->subdivx = atoi(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"subdivy", 0);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(val[0]) gr->subdivy = atoi(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"divx", 0);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(val[0]) gr->divx = atoi(val);
|
2022-08-03 10:44:34 +02:00
|
|
|
if(gr->divx < 1) gr->divx = 1;
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"divy", 0);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(val[0]) gr->divy = atoi(val);
|
2022-08-03 10:44:34 +02:00
|
|
|
if(gr->divy < 1) gr->divy = 1;
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"linewidth_mult", 0);
|
2023-04-27 20:55:17 +02:00
|
|
|
if(val[0]) gr->linewidth_mult = atof(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"rainbow", 0);
|
2022-10-13 01:00:55 +02:00
|
|
|
if(val[0] == '1') gr->rainbow = 1;
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"logx", 0);
|
2022-08-05 00:57:03 +02:00
|
|
|
if(val[0] == '1') gr->logx = 1;
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"logy", 0);
|
2022-08-05 00:57:03 +02:00
|
|
|
if(val[0] == '1') gr->logy = 1;
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"y1", 0);
|
2025-01-10 01:52:54 +01:00
|
|
|
if(val[0]) gr->gy1 = atof_eng(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"y2", 0);
|
2025-01-10 01:52:54 +01:00
|
|
|
if(val[0]) gr->gy2 = atof_eng(val);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(gr->gy1 == gr->gy2) gr->gy2 += 1.0;
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"digital", 0);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(val[0]) gr->digital = atoi(val);
|
|
|
|
|
if(gr->digital) {
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"ypos1", 0);
|
2025-01-10 01:52:54 +01:00
|
|
|
if(val[0]) gr->ypos1 = atof_eng(val);
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"ypos2", 0);
|
2025-01-10 01:52:54 +01:00
|
|
|
if(val[0]) gr->ypos2 = atof_eng(val);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(gr->ypos2 == gr->ypos1) gr->ypos2 += 1.0;
|
2022-01-03 02:28:34 +01:00
|
|
|
}
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->posh = gr->ypos2 - gr->ypos1;
|
|
|
|
|
|
2021-12-25 05:15:52 +01:00
|
|
|
/* plot single dataset */
|
2024-11-25 14:29:19 +01:00
|
|
|
val = get_tok_value(r->prop_ptr,"dataset", 0);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(val[0]) gr->dataset = atoi(val);
|
|
|
|
|
gr->gh = gr->gy2 - gr->gy1;
|
2021-12-27 18:42:56 +01:00
|
|
|
/* set margins */
|
2022-01-05 23:07:08 +01:00
|
|
|
tmp = gr->rw * 0.14;
|
2025-02-21 12:53:04 +01:00
|
|
|
gr->marginx = tmp;
|
2022-01-05 23:07:08 +01:00
|
|
|
tmp = gr->rh * 0.14;
|
2025-02-21 12:53:04 +01:00
|
|
|
gr->marginy = tmp;
|
2022-01-05 23:07:08 +01:00
|
|
|
/* calculate graph bounding box (container - margin)
|
|
|
|
|
* This is the box where plot is done */
|
|
|
|
|
gr->x1 = gr->rx1 + gr->marginx;
|
|
|
|
|
gr->x2 = gr->rx2 - gr->marginx * 0.35; /* less space for right margin */
|
|
|
|
|
if(gr->digital) gr->y1 = gr->ry1 + gr->marginy * 0.4; /* less top space for digital graphs */
|
|
|
|
|
else gr->y1 = gr->ry1 + gr->marginy;
|
|
|
|
|
gr->y2 = gr->ry2 - gr->marginy;
|
|
|
|
|
gr->w = gr->x2 - gr->x1;
|
|
|
|
|
gr->h = gr->y2 - gr->y1;
|
|
|
|
|
|
|
|
|
|
/* label text size calculations */
|
2025-01-07 04:20:32 +01:00
|
|
|
gr->txtsizelab = gr->marginy * 0.006;
|
2022-02-03 00:40:59 +01:00
|
|
|
/*
|
|
|
|
|
* tmp = gr->w * 0.00044;
|
|
|
|
|
* if(tmp < gr->txtsizelab) gr->txtsizelab = tmp;
|
|
|
|
|
*/
|
2023-02-26 11:22:19 +01:00
|
|
|
if(xctx->graph_flags & 2)
|
2022-01-29 02:52:26 +01:00
|
|
|
gr->digtxtsizelab = 0.000900 * fabs( gr->h / gr->posh * gr->gh );
|
2022-01-05 17:38:01 +01:00
|
|
|
else
|
2022-01-29 02:52:26 +01:00
|
|
|
gr->digtxtsizelab = 0.001200 * fabs( gr->h / gr->posh * gr->gh );
|
2025-01-10 01:52:54 +01:00
|
|
|
gr->txtsizelab *= gr->magx;
|
2022-01-05 23:07:08 +01:00
|
|
|
|
|
|
|
|
/* x axis, y axis text sizes */
|
2025-01-07 04:20:32 +01:00
|
|
|
gr->txtsizey = gr->h / gr->divy * 0.0095;
|
2025-02-22 10:33:13 +01:00
|
|
|
tmp = gr->marginx * 0.004;
|
2022-01-05 23:07:08 +01:00
|
|
|
if(tmp < gr->txtsizey) gr->txtsizey = tmp;
|
2025-02-22 10:33:13 +01:00
|
|
|
/* tmp = gr->marginy * 0.02;
|
|
|
|
|
* if(tmp < gr->txtsizey) gr->txtsizey = tmp;
|
|
|
|
|
*/
|
2023-05-20 23:21:43 +02:00
|
|
|
gr->txtsizey *= gr->magy;
|
2022-01-05 23:07:08 +01:00
|
|
|
|
2025-02-22 10:33:13 +01:00
|
|
|
gr->txtsizex = gr->w / gr->divx * 0.0070;
|
|
|
|
|
tmp = gr->marginy * 0.0065;
|
2022-01-05 23:07:08 +01:00
|
|
|
if(tmp < gr->txtsizex) gr->txtsizex = tmp;
|
2023-05-20 23:21:43 +02:00
|
|
|
gr->txtsizex *= gr->magx;
|
2022-01-05 23:07:08 +01:00
|
|
|
|
2022-01-01 15:46:03 +01:00
|
|
|
/* cache coefficients for faster graph --> xschem coord transformations */
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->cx = gr->w / gr->gw;
|
|
|
|
|
gr->dx = gr->x1 - gr->gx1 * gr->cx;
|
|
|
|
|
gr->cy = -gr->h / gr->gh;
|
|
|
|
|
gr->dy = gr->y2 - gr->gy1 * gr->cy;
|
2022-01-02 22:24:44 +01:00
|
|
|
/* graph --> xschem transform for digital waves y axis */
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->dcy = -gr->h / gr->posh;
|
|
|
|
|
gr->ddy = gr->y2 - gr->ypos1 * gr->dcy;
|
2021-12-22 04:39:23 +01:00
|
|
|
|
2022-01-01 15:46:03 +01:00
|
|
|
/* direct graph --> screen transform */
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->scx = gr->cx * xctx->mooz;
|
|
|
|
|
gr->sdx = (gr->dx + xctx->xorigin) * xctx->mooz;
|
|
|
|
|
gr->scy = gr->cy * xctx->mooz;
|
|
|
|
|
gr->sdy = (gr->dy + xctx->yorigin) * xctx->mooz;
|
2022-01-02 22:24:44 +01:00
|
|
|
/* direct graph --> screen for digital waves y axis */
|
2022-01-05 23:07:08 +01:00
|
|
|
gr->dscy = gr->dcy * xctx->mooz;
|
|
|
|
|
gr->dsdy = (gr->ddy + xctx->yorigin) * xctx->mooz;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 19:20:51 +01:00
|
|
|
static void draw_cursor(double active_cursorx, double other_cursorx, int cursor_color, Graph_ctx *gr)
|
2022-01-05 23:07:08 +01:00
|
|
|
{
|
|
|
|
|
|
2024-04-01 00:25:50 +02:00
|
|
|
double xx, pos = active_cursorx;
|
2022-04-28 00:00:51 +02:00
|
|
|
double tx1, ty1, tx2, ty2, dtmp;
|
2024-03-30 19:20:51 +01:00
|
|
|
int tmp;
|
2022-09-29 14:22:33 +02:00
|
|
|
char tmpstr[100];
|
2022-01-05 23:07:08 +01:00
|
|
|
double txtsize = gr->txtsizex;
|
2024-03-30 19:20:51 +01:00
|
|
|
short flip = (other_cursorx > active_cursorx) ? 0 : 1;
|
|
|
|
|
int xoffs = flip ? 3 : -3;
|
2022-01-05 23:07:08 +01:00
|
|
|
|
2024-04-01 00:25:50 +02:00
|
|
|
if(gr->logx) pos = mylog10(pos);
|
|
|
|
|
xx = W_X(pos);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(xx >= gr->x1 && xx <= gr->x2) {
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(cursor_color, NOW, xx, gr->ry1, xx, gr->ry2, 0.0, 1, NULL);
|
2022-01-05 23:07:08 +01:00
|
|
|
if(gr->unitx != 1.0)
|
2022-06-29 11:47:10 +02:00
|
|
|
my_snprintf(tmpstr, S(tmpstr), "%.5g%c", gr->unitx * active_cursorx , gr->unitx_suffix);
|
2022-01-05 23:07:08 +01:00
|
|
|
else
|
2022-09-29 14:22:33 +02:00
|
|
|
my_snprintf(tmpstr, S(tmpstr), "%s", dtoa_eng(active_cursorx));
|
2022-04-28 00:00:51 +02:00
|
|
|
text_bbox(tmpstr, txtsize, txtsize, 2, flip, 0, 0, xx + xoffs, gr->ry2-1, &tx1, &ty1, &tx2, &ty2, &tmp, &dtmp);
|
2025-02-18 02:25:55 +01:00
|
|
|
filledrect(0, NOW, tx1, ty1, tx2, ty2, 2, -1, -1);
|
2022-01-05 23:07:08 +01:00
|
|
|
draw_string(cursor_color, NOW, tmpstr, 2, flip, 0, 0, xx + xoffs, gr->ry2-1, txtsize, txtsize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-31 13:27:12 +02:00
|
|
|
static void draw_cursor_difference(double c1, double c2, Graph_ctx *gr)
|
2022-01-05 23:07:08 +01:00
|
|
|
{
|
|
|
|
|
int tmp;
|
2022-09-29 14:22:33 +02:00
|
|
|
char tmpstr[100];
|
2022-01-05 23:07:08 +01:00
|
|
|
double txtsize = gr->txtsizex;
|
|
|
|
|
double tx1, ty1, tx2, ty2;
|
2024-04-01 00:25:50 +02:00
|
|
|
double cc1 = gr->logx ? mylog10(c1) : c1;
|
|
|
|
|
double cc2 = gr->logx ? mylog10(c2) : c2;
|
|
|
|
|
double aa = W_X(cc1);
|
2022-01-05 23:07:08 +01:00
|
|
|
double a = CLIP(aa, gr->x1, gr->x2);
|
2024-04-01 00:25:50 +02:00
|
|
|
double bb = W_X(cc2);
|
2022-01-05 23:07:08 +01:00
|
|
|
double b = CLIP(bb, gr->x1, gr->x2);
|
|
|
|
|
double diff = fabs(b - a);
|
2024-01-04 01:41:40 +01:00
|
|
|
double diffw;
|
2022-01-05 23:07:08 +01:00
|
|
|
double xx = ( a + b ) * 0.5;
|
|
|
|
|
double yy = gr->ry2 - 1;
|
2022-04-28 00:00:51 +02:00
|
|
|
double dtmp;
|
2022-01-05 23:07:08 +01:00
|
|
|
double yline;
|
2024-01-04 01:41:40 +01:00
|
|
|
|
|
|
|
|
|
2024-04-01 00:25:50 +02:00
|
|
|
diffw = fabs(c2 - c1);
|
2024-01-04 01:41:40 +01:00
|
|
|
|
2022-01-05 23:07:08 +01:00
|
|
|
if(gr->unitx != 1.0)
|
|
|
|
|
my_snprintf(tmpstr, S(tmpstr), "%.4g%c", gr->unitx * diffw , gr->unitx_suffix);
|
|
|
|
|
else
|
2022-09-29 14:22:33 +02:00
|
|
|
my_snprintf(tmpstr, S(tmpstr), "%s", dtoa_eng(diffw));
|
2022-04-28 00:00:51 +02:00
|
|
|
text_bbox(tmpstr, txtsize, txtsize, 2, 0, 1, 0, xx, yy, &tx1, &ty1, &tx2, &ty2, &tmp, &dtmp);
|
2022-01-05 23:07:08 +01:00
|
|
|
if( tx2 - tx1 < diff ) {
|
|
|
|
|
draw_string(3, NOW, tmpstr, 2, 0, 1, 0, xx, yy, txtsize, txtsize);
|
|
|
|
|
if( a > b) {
|
2022-04-28 00:00:51 +02:00
|
|
|
dtmp = a; a = b; b = dtmp;
|
2022-01-05 23:07:08 +01:00
|
|
|
}
|
|
|
|
|
yline = (ty1 + ty2) * 0.5;
|
2025-11-29 00:47:46 +01:00
|
|
|
if( tx1 - a > 4.0) drawline(3, NOW, a + 2, yline, tx1 - 2, yline, 0.0, 1, NULL);
|
|
|
|
|
if( b - tx2 > 4.0) drawline(3, NOW, tx2 + 2, yline, b - 2, yline, 0.0, 1, NULL);
|
2022-01-05 23:07:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-07 04:20:32 +01:00
|
|
|
static void draw_hcursor(double active_cursory, int cursor_color, Graph_ctx *gr)
|
2025-01-06 13:14:12 +01:00
|
|
|
{
|
|
|
|
|
double yy, pos = active_cursory;
|
|
|
|
|
double tx1, ty1, tx2, ty2, dtmp;
|
|
|
|
|
int tmp;
|
|
|
|
|
char tmpstr[100];
|
|
|
|
|
double txtsize = gr->txtsizey;
|
|
|
|
|
double th;
|
|
|
|
|
|
2025-01-07 04:20:32 +01:00
|
|
|
if(gr->digital) return;
|
2025-01-06 13:14:12 +01:00
|
|
|
if(gr->logy) pos = mylog10(pos);
|
|
|
|
|
yy = W_Y(pos);
|
|
|
|
|
if(yy >= gr->y1 && yy <= gr->y2) {
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(cursor_color, NOW, gr->rx1 + 10, yy, gr->rx2 - 10, yy, 0.0, 1, NULL);
|
2025-01-06 13:14:12 +01:00
|
|
|
if(gr->unity != 1.0)
|
|
|
|
|
my_snprintf(tmpstr, S(tmpstr), " %.5g%c ", gr->unity * active_cursory , gr->unity_suffix);
|
|
|
|
|
else
|
|
|
|
|
my_snprintf(tmpstr, S(tmpstr), " %s ", dtoa_eng(active_cursory));
|
|
|
|
|
text_bbox(tmpstr, txtsize, txtsize, 0, 0, 0, 0, gr->rx1 + 5, yy, &tx1, &ty1, &tx2, &ty2, &tmp, &dtmp);
|
|
|
|
|
th = (ty2 - ty1) / 2.; /* half text height */
|
|
|
|
|
ty1 -= th;
|
|
|
|
|
ty2 -= th;
|
2025-02-18 02:25:55 +01:00
|
|
|
filledrect(0, NOW, tx1, ty1, tx2, ty2, 2, -1, -1);
|
2025-01-06 13:14:12 +01:00
|
|
|
draw_string(cursor_color, NOW, tmpstr, 0, 0, 0, 0, gr->rx1 + 5, yy - th, txtsize, txtsize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void draw_hcursor_difference(double c1, double c2, Graph_ctx *gr)
|
|
|
|
|
{
|
2025-01-07 04:20:32 +01:00
|
|
|
int tmp;
|
|
|
|
|
char tmpstr[100];
|
|
|
|
|
double txtsize = gr->txtsizey;
|
|
|
|
|
double tx1, ty1, tx2, ty2;
|
|
|
|
|
double cc1 = gr->logy ? mylog10(c1) : c1;
|
|
|
|
|
double cc2 = gr->logy ? mylog10(c2) : c2;
|
|
|
|
|
double aa = W_Y(cc1);
|
|
|
|
|
double a = CLIP(aa, gr->y1, gr->y2);
|
|
|
|
|
double bb = W_Y(cc2);
|
|
|
|
|
double b = CLIP(bb, gr->y1, gr->y2);
|
|
|
|
|
double diff = fabs(b - a);
|
|
|
|
|
double diffh;
|
|
|
|
|
double yy = ( a + b ) * 0.5;
|
|
|
|
|
double xx = gr->rx1 + 5;
|
|
|
|
|
double dtmp;
|
|
|
|
|
double xline;
|
|
|
|
|
|
|
|
|
|
if(gr->digital) return;
|
|
|
|
|
diffh = fabs(c2 - c1);
|
|
|
|
|
if(gr->unity != 1.0)
|
|
|
|
|
my_snprintf(tmpstr, S(tmpstr), " %.4g%c ", gr->unity * diffh , gr->unity_suffix);
|
|
|
|
|
else
|
|
|
|
|
my_snprintf(tmpstr, S(tmpstr), " %s ", dtoa_eng(diffh));
|
|
|
|
|
text_bbox(tmpstr, txtsize, txtsize, 0, 0, 0, 1, xx, yy, &tx1, &ty1, &tx2, &ty2, &tmp, &dtmp);
|
|
|
|
|
if( 2 * (ty2 - ty1) < diff ) {
|
2025-02-18 02:25:55 +01:00
|
|
|
filledrect(0, NOW, tx1, ty1, tx2, ty2, 2, -1, -1);
|
2025-01-07 04:20:32 +01:00
|
|
|
draw_string(3, NOW, tmpstr, 0, 0, 0, 1, xx, yy, txtsize, txtsize);
|
|
|
|
|
if( a > b) {
|
|
|
|
|
dtmp = a; a = b; b = dtmp;
|
|
|
|
|
}
|
|
|
|
|
xline = tx1 + 10;
|
2025-11-29 00:47:46 +01:00
|
|
|
if( ty1 - a > 4.0) drawline(3, NOW, xline, a + 2, xline, ty1 - 2, 0.0, 1, NULL);
|
|
|
|
|
if( b - ty2 > 4.0) drawline(3, NOW, xline, ty2 + 2, xline, b - 2, 0.0, 1, NULL);
|
2025-01-07 04:20:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-06 13:14:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-01-05 23:07:08 +01:00
|
|
|
/* sweep variables on x-axis, node labels */
|
|
|
|
|
static void draw_graph_variables(int wcnt, int wave_color, int n_nodes, int sweep_idx,
|
|
|
|
|
int flags, const char *ntok, const char *stok, const char *bus_msb, Graph_ctx *gr)
|
|
|
|
|
{
|
|
|
|
|
char tmpstr[1024];
|
|
|
|
|
|
|
|
|
|
/* clipping everything outside container area */
|
|
|
|
|
bbox(START, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
bbox(ADD, gr->rx1, gr->ry1, gr->rx2, gr->ry2);
|
|
|
|
|
bbox(SET_INSIDE, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
/* draw sweep variable(s) on x-axis */
|
|
|
|
|
if(wcnt == 0 || (stok && stok[0])) {
|
2023-10-11 14:05:27 +02:00
|
|
|
if(sch_waves_loaded() >= 0) stok = xctx->raw->names[sweep_idx];
|
2022-01-05 23:07:08 +01:00
|
|
|
if(gr->unitx != 1.0) my_snprintf(tmpstr, S(tmpstr), "%s[%c]", stok ? stok : "" , gr->unitx_suffix);
|
|
|
|
|
else my_snprintf(tmpstr, S(tmpstr), "%s", stok ? stok : "");
|
|
|
|
|
draw_string(wave_color, NOW, tmpstr, 2, 1, 0, 0,
|
2025-02-21 12:53:04 +01:00
|
|
|
gr->rx1 + 2 + gr->rw / n_nodes * wcnt, gr->ry2-2, gr->txtsizelab, gr->txtsizelab);
|
2022-01-05 23:07:08 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-25 14:29:19 +01:00
|
|
|
if(gr->legend || gr->digital) {
|
|
|
|
|
/* draw node labels in graph */
|
|
|
|
|
if(bus_msb) {
|
|
|
|
|
if(gr->unity != 1.0) my_snprintf(tmpstr, S(tmpstr), "%s[%c]",
|
|
|
|
|
find_nth(ntok, ";,", "\"", 0, 1), gr->unity_suffix);
|
|
|
|
|
else my_snprintf(tmpstr, S(tmpstr), "%s",find_nth(ntok, ";,", "\"", 0, 1));
|
|
|
|
|
} else {
|
|
|
|
|
char *ntok_ptr = NULL;
|
|
|
|
|
char *alias_ptr = NULL;
|
|
|
|
|
dbg(1, "ntok=%s\n", ntok);
|
|
|
|
|
if(strstr(ntok, ";")) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &alias_ptr, find_nth(ntok, ";", "\"", 0, 1));
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &ntok_ptr, find_nth(ntok, ";", "\"", 0, 2));
|
2024-11-25 14:29:19 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &alias_ptr, ntok);
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &ntok_ptr, ntok);
|
2024-11-25 14:29:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(gr->unity != 1.0) my_snprintf(tmpstr, S(tmpstr), "%s[%c]", alias_ptr, gr->unity_suffix);
|
|
|
|
|
else my_snprintf(tmpstr, S(tmpstr), "%s", alias_ptr);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &alias_ptr);
|
|
|
|
|
my_free(_ALLOC_ID_, &ntok_ptr);
|
2024-11-25 14:29:19 +01:00
|
|
|
}
|
|
|
|
|
if(gr->digital) {
|
|
|
|
|
double xt = gr->x1 - 15 * gr->txtsizelab;
|
|
|
|
|
double s1 = DIG_NWAVES; /* 1/DIG_NWAVES waveforms fit in graph if unscaled vertically */
|
|
|
|
|
double s2 = DIG_SPACE; /* (DIG_NWAVES - DIG_SPACE) spacing between traces */
|
|
|
|
|
double yt;
|
|
|
|
|
if(flags & 2) /* cursor1 with measures */
|
|
|
|
|
yt = s1 * (double)(n_nodes - wcnt) * gr->gh + gr->gh * 0.4 * s2;
|
|
|
|
|
else
|
|
|
|
|
yt = s1 * (double)(n_nodes - wcnt) * gr->gh + gr->gh * 0.1 * s2;
|
|
|
|
|
|
|
|
|
|
if(yt <= gr->ypos2 && yt >= gr->ypos1) {
|
|
|
|
|
#if HAS_CAIRO == 1
|
|
|
|
|
if(gr->hilight_wave == wcnt) {
|
|
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create("Sans-Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
draw_string(wave_color, NOW, tmpstr, 2, 0, 0, 0,
|
|
|
|
|
xt, DW_Y(yt), gr->digtxtsizelab * gr->magy, gr->digtxtsizelab * gr->magy);
|
|
|
|
|
#if HAS_CAIRO == 1
|
|
|
|
|
if(gr->hilight_wave == wcnt) {
|
|
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create("Sans-Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-01-29 02:36:54 +01:00
|
|
|
#if HAS_CAIRO == 1
|
2022-08-27 12:56:33 +02:00
|
|
|
if(gr->hilight_wave == wcnt) {
|
2022-12-25 10:42:07 +01:00
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create("Sans-Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font);
|
2022-12-25 23:13:59 +01:00
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
2022-01-28 04:44:07 +01:00
|
|
|
}
|
2022-01-29 02:36:54 +01:00
|
|
|
#endif
|
2024-11-25 14:29:19 +01:00
|
|
|
draw_string(wave_color, NOW, tmpstr, 0, 0, 0, 0,
|
|
|
|
|
gr->rx1 + 2 + gr->rw / n_nodes * wcnt, gr->ry1, gr->txtsizelab, gr->txtsizelab);
|
2022-01-29 02:36:54 +01:00
|
|
|
#if HAS_CAIRO == 1
|
2022-08-27 12:56:33 +02:00
|
|
|
if(gr->hilight_wave == wcnt) {
|
2022-12-25 10:42:07 +01:00
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create("Sans-Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font);
|
2022-12-25 23:13:59 +01:00
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
2022-01-28 04:44:07 +01:00
|
|
|
}
|
2022-01-29 02:36:54 +01:00
|
|
|
#endif
|
2022-01-05 23:07:08 +01:00
|
|
|
}
|
2024-11-25 14:29:19 +01:00
|
|
|
} /* if(gr->legend) */
|
2022-01-05 23:07:08 +01:00
|
|
|
bbox(END, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 19:20:51 +01:00
|
|
|
static void show_node_measures(int measure_p, double measure_x, double measure_prev_x,
|
|
|
|
|
const char *bus_msb, int wave_color, int idx, SPICE_DATA **idx_arr,
|
2024-09-05 00:51:15 +02:00
|
|
|
int n_bits, int n_nodes, const char *ntok, int wcnt, Graph_ctx *gr, xRect *r, double cursor1)
|
2022-01-05 23:07:08 +01:00
|
|
|
{
|
|
|
|
|
char tmpstr[1024];
|
2022-01-08 01:16:13 +01:00
|
|
|
double yy;
|
2022-01-05 23:07:08 +01:00
|
|
|
/* show values of signals if cursor1 active */
|
2022-02-14 22:32:45 +01:00
|
|
|
if(idx == -1) return;
|
2023-10-11 14:05:27 +02:00
|
|
|
if(!xctx->raw) {
|
|
|
|
|
dbg(0, "show_node_measures(): no raw struct allocated\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-11-25 14:29:19 +01:00
|
|
|
if(!gr->legend && !gr->digital) return;
|
2024-03-30 19:20:51 +01:00
|
|
|
if(measure_p >= 0) {
|
|
|
|
|
|
|
|
|
|
/* draw node values in graph */
|
|
|
|
|
bbox(START, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
bbox(ADD, gr->rx1, gr->ry1, gr->rx2, gr->ry2);
|
|
|
|
|
bbox(SET_INSIDE, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
if(!bus_msb) {
|
|
|
|
|
double diffy;
|
|
|
|
|
double diffx;
|
|
|
|
|
char *fmt1, *fmt2;
|
|
|
|
|
double yy1;
|
2024-09-05 00:51:15 +02:00
|
|
|
|
|
|
|
|
if( gr->logx) cursor1 = mylog10(cursor1);
|
2024-03-30 19:20:51 +01:00
|
|
|
yy1 = xctx->raw->values[idx][measure_p-1];
|
|
|
|
|
diffy = xctx->raw->values[idx][measure_p] - yy1;
|
|
|
|
|
diffx = measure_x - measure_prev_x;
|
2024-04-01 00:25:50 +02:00
|
|
|
yy = yy1 + diffy / diffx * (cursor1 - measure_prev_x);
|
2024-04-01 11:42:54 +02:00
|
|
|
if(XSIGN0(gr->gy1) != XSIGN0(gr->gy2) && fabs(yy) < 1e-12 * fabs(gr->gh)) yy = 0.0;
|
2024-03-30 19:20:51 +01:00
|
|
|
if(yy != 0.0 && fabs(yy * gr->unity) < 1.0e-3) {
|
|
|
|
|
fmt1="%.2e";
|
|
|
|
|
fmt2="%.2e%c";
|
|
|
|
|
} else {
|
|
|
|
|
fmt1="%.4g";
|
|
|
|
|
fmt2="%.4g%c";
|
|
|
|
|
}
|
|
|
|
|
if(gr->unity != 1.0) my_snprintf(tmpstr, S(tmpstr), fmt2, yy * gr->unity, gr->unity_suffix);
|
|
|
|
|
else my_snprintf(tmpstr, S(tmpstr), fmt1, yy);
|
2022-01-05 23:07:08 +01:00
|
|
|
} else {
|
2024-03-30 19:20:51 +01:00
|
|
|
double vthl, vthh;
|
|
|
|
|
int hex_digits = ((n_bits - 1) >> 2) + 1;
|
|
|
|
|
vthh = gr->gy1 * 0.2 + gr->gy2 * 0.8;
|
|
|
|
|
vthl = gr->gy1 * 0.8 + gr->gy2 * 0.2;
|
|
|
|
|
get_bus_value(n_bits, hex_digits, idx_arr, measure_p - 1, tmpstr, vthl, vthh);
|
|
|
|
|
}
|
|
|
|
|
if(!bus_msb && !gr->digital) {
|
|
|
|
|
draw_string(wave_color, NOW, tmpstr, 0, 0, 0, 0,
|
|
|
|
|
gr->rx1 + 2 + gr->rw / n_nodes * wcnt, gr->ry1 + gr->txtsizelab * 60,
|
|
|
|
|
gr->txtsizelab * 0.8, gr->txtsizelab * 0.8);
|
|
|
|
|
dbg(1, "node: %s, x=%g, value=%g\n", ntok, measure_x, yy);
|
|
|
|
|
}
|
|
|
|
|
else if(gr->digital) {
|
|
|
|
|
double xt = gr->x1 - 15 * gr->txtsizelab;
|
|
|
|
|
double s1 = DIG_NWAVES; /* 1/DIG_NWAVES waveforms fit in graph if unscaled vertically */
|
|
|
|
|
double s2 = DIG_SPACE; /* (DIG_NWAVES - DIG_SPACE) spacing between traces */
|
|
|
|
|
double yt = s1 * (double)(n_nodes - wcnt) * gr->gh + gr->gh * 0.4 * s2;
|
|
|
|
|
if(yt <= gr->ypos2 && yt >= gr->ypos1) {
|
|
|
|
|
draw_string(wave_color, NOW, tmpstr, 2, 0, 0, 0,
|
|
|
|
|
xt, DW_Y(yt) + gr->digtxtsizelab * 50, gr->digtxtsizelab * 0.8, gr->digtxtsizelab * 0.8);
|
|
|
|
|
}
|
2022-01-05 23:07:08 +01:00
|
|
|
}
|
2024-03-30 19:20:51 +01:00
|
|
|
bbox(END, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
} /* if(measure_p >= 0) */
|
2022-01-05 23:07:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-01-21 19:17:43 +01:00
|
|
|
int embed_rawfile(const char *rawfile)
|
|
|
|
|
{
|
|
|
|
|
int res = 0;
|
|
|
|
|
size_t len;
|
|
|
|
|
char *ptr;
|
|
|
|
|
|
2024-02-22 14:36:34 +01:00
|
|
|
dbg(1, "embed_rawfile(): rawfile=%s\n", rawfile);
|
2022-01-21 19:17:43 +01:00
|
|
|
if(xctx->lastsel==1 && xctx->sel_array[0].type==ELEMENT) {
|
|
|
|
|
xInstance *i = &xctx->inst[xctx->sel_array[0].n];
|
|
|
|
|
xctx->push_undo();
|
|
|
|
|
ptr = base64_from_file(rawfile, &len);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &i->prop_ptr, subst_token(i->prop_ptr, "spice_data", ptr));
|
|
|
|
|
my_free(_ALLOC_ID_, &ptr);
|
2023-05-13 13:13:35 +02:00
|
|
|
set_modify(1);
|
2022-01-21 19:17:43 +01:00
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-27 20:47:27 +01:00
|
|
|
/* when double clicking in a graph if this happens on a wave label
|
2022-01-27 21:14:52 +01:00
|
|
|
* what == 1:
|
|
|
|
|
* look up the wave and call tcl "graph_edit_wave <graph> <wave>"
|
|
|
|
|
* with graph index and wave index
|
|
|
|
|
* what == 2:
|
|
|
|
|
* look up the wave and draw in bold
|
2022-01-27 20:47:27 +01:00
|
|
|
* return 1 if a wave was found
|
|
|
|
|
*/
|
2022-01-27 21:14:52 +01:00
|
|
|
int edit_wave_attributes(int what, int i, Graph_ctx *gr)
|
2022-01-27 20:47:27 +01:00
|
|
|
{
|
|
|
|
|
char *node = NULL, *color = NULL, *sweep = NULL;
|
|
|
|
|
int sweep_idx = 0;
|
|
|
|
|
int n_nodes; /* number of variables to display in a single graph */
|
|
|
|
|
char *saven, *savec, *saves, *nptr, *cptr, *sptr;
|
|
|
|
|
const char *ntok, *ctok, *stok;
|
|
|
|
|
int wcnt = 0, ret = 0;
|
|
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
|
|
|
|
|
|
|
|
|
/* get plot data */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node, get_tok_value(r->prop_ptr,"node", 0));
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &color, get_tok_value(r->prop_ptr,"color", 0));
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &sweep, get_tok_value(r->prop_ptr,"sweep", 0));
|
2022-01-27 20:47:27 +01:00
|
|
|
nptr = node;
|
|
|
|
|
cptr = color;
|
|
|
|
|
sptr = sweep;
|
2024-12-05 02:54:08 +01:00
|
|
|
n_nodes = count_items(node, "\n", "\"");
|
2022-01-27 20:47:27 +01:00
|
|
|
/* process each node given in "node" attribute, get also associated color/sweep var if any */
|
2024-12-05 02:54:08 +01:00
|
|
|
while( (ntok = my_strtok_r(nptr, "\n", "\"", 0, &saven)) ) {
|
2023-10-11 14:05:27 +02:00
|
|
|
ctok = my_strtok_r(cptr, " ", "", 0, &savec);
|
|
|
|
|
stok = my_strtok_r(sptr, "\t\n ", "\"", 0, &saves);
|
2022-01-27 20:47:27 +01:00
|
|
|
nptr = cptr = sptr = NULL;
|
2025-01-04 11:33:58 +01:00
|
|
|
dbg(1, "ntok=%s ctok=%s\n", ntok, ctok? ctok: "<NULL>");
|
2022-01-27 20:47:27 +01:00
|
|
|
if(stok && stok[0]) {
|
2024-02-25 12:21:13 +01:00
|
|
|
sweep_idx = get_raw_index(stok, NULL);
|
2022-01-27 20:47:27 +01:00
|
|
|
if( sweep_idx == -1) sweep_idx = 0;
|
|
|
|
|
}
|
|
|
|
|
if(gr->digital) {
|
|
|
|
|
double xt1 = gr->rx1; /* <-- waves_selected() is more restrictive than this */
|
|
|
|
|
double xt2 = gr->x1 - 20 * gr->txtsizelab;
|
|
|
|
|
double s1 = DIG_NWAVES; /* 1/DIG_NWAVES waveforms fit in graph if unscaled vertically */
|
|
|
|
|
double s2 = DIG_SPACE; /* (DIG_NWAVES - DIG_SPACE) spacing between traces */
|
2022-01-30 03:10:09 +01:00
|
|
|
double yt1 = s1 * (double)(n_nodes - wcnt) * gr->gh - gr->gy1 * s2;
|
2022-01-27 20:47:27 +01:00
|
|
|
double yt2 = yt1 + s1 * gr->gh;
|
|
|
|
|
if(yt1 <= gr->ypos2 && yt1 >= gr->ypos1) {
|
|
|
|
|
double tmp = DW_Y(yt1);
|
|
|
|
|
yt1 = DW_Y(yt2);
|
|
|
|
|
yt2 = tmp;
|
|
|
|
|
if(POINTINSIDE(xctx->mousex_snap, xctx->mousey_snap, xt1, yt1, xt2, yt2)) {
|
|
|
|
|
char s[30];
|
|
|
|
|
ret = 1;
|
2022-01-27 21:14:52 +01:00
|
|
|
if(what == 1) {
|
2022-08-27 12:56:33 +02:00
|
|
|
int save = gr->hilight_wave;
|
2022-01-27 21:14:52 +01:00
|
|
|
my_snprintf(s, S(s), "%d %d", i, wcnt);
|
2022-08-27 12:56:33 +02:00
|
|
|
gr->hilight_wave = wcnt;
|
2022-01-27 21:14:52 +01:00
|
|
|
tclvareval("graph_edit_wave ", s, NULL);
|
2022-08-27 12:56:33 +02:00
|
|
|
gr->hilight_wave = save;
|
2022-01-28 04:44:07 +01:00
|
|
|
} else {
|
2022-08-27 12:56:33 +02:00
|
|
|
if(gr->hilight_wave == wcnt) {
|
|
|
|
|
gr->hilight_wave = -1;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &r->prop_ptr,
|
2025-02-15 00:50:02 +01:00
|
|
|
subst_token(r->prop_ptr, "hilight_wave", my_itoa(gr->hilight_wave)));
|
2022-01-28 04:44:07 +01:00
|
|
|
} else {
|
2022-08-27 12:56:33 +02:00
|
|
|
gr->hilight_wave = wcnt;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &r->prop_ptr,
|
2025-02-15 00:50:02 +01:00
|
|
|
subst_token(r->prop_ptr, "hilight_wave", my_itoa(gr->hilight_wave)));
|
2022-01-28 04:44:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-27 20:47:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
double xt1 = gr->rx1 + 2 + gr->rw / n_nodes * wcnt;
|
|
|
|
|
double yt1 = gr->ry1;
|
|
|
|
|
double xt2 = xt1 + gr->rw / n_nodes;
|
|
|
|
|
double yt2 = gr->y1;
|
|
|
|
|
if(POINTINSIDE(xctx->mousex_snap, xctx->mousey_snap, xt1, yt1, xt2, yt2)) {
|
|
|
|
|
char s[50];
|
|
|
|
|
ret = 1;
|
2022-01-27 21:14:52 +01:00
|
|
|
if(what == 1) {
|
2022-08-27 12:56:33 +02:00
|
|
|
int save = gr->hilight_wave;
|
2022-01-27 21:14:52 +01:00
|
|
|
my_snprintf(s, S(s), "%d %d", i, wcnt);
|
2022-08-27 12:56:33 +02:00
|
|
|
gr->hilight_wave = wcnt;
|
2022-01-27 21:14:52 +01:00
|
|
|
tclvareval("graph_edit_wave ", s, NULL);
|
2022-08-27 12:56:33 +02:00
|
|
|
gr->hilight_wave = save;
|
2022-01-28 04:44:07 +01:00
|
|
|
} else {
|
2022-08-27 12:56:33 +02:00
|
|
|
if(gr->hilight_wave == wcnt) {
|
|
|
|
|
gr->hilight_wave = -1;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &r->prop_ptr,
|
2025-02-15 00:50:02 +01:00
|
|
|
subst_token(r->prop_ptr, "hilight_wave", my_itoa(gr->hilight_wave)));
|
2022-01-28 04:44:07 +01:00
|
|
|
} else {
|
2022-08-27 12:56:33 +02:00
|
|
|
gr->hilight_wave = wcnt;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &r->prop_ptr,
|
2025-02-15 00:50:02 +01:00
|
|
|
subst_token(r->prop_ptr, "hilight_wave", my_itoa(gr->hilight_wave)));
|
2022-01-28 04:44:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-27 20:47:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-02-18 09:44:11 +01:00
|
|
|
++wcnt;
|
2023-10-11 14:05:27 +02:00
|
|
|
} /* while( (ntok = my_strtok_r(nptr, "\n\t ", "", 0, &saven)) ) */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &node);
|
|
|
|
|
my_free(_ALLOC_ID_, &color);
|
|
|
|
|
my_free(_ALLOC_ID_, &sweep);
|
2022-01-27 20:47:27 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-03 03:35:55 +01:00
|
|
|
/* derived from draw_graph(), used to calculate y range of custom equation graph data,
|
|
|
|
|
* call the plot_raw_custom_data
|
2022-02-17 01:22:15 +01:00
|
|
|
* handling multiple datasets ad wraps (as in multi-sweep DC sims).
|
|
|
|
|
*/
|
|
|
|
|
int calc_custom_data_yrange(int sweep_idx, const char *express, Graph_ctx *gr)
|
|
|
|
|
{
|
|
|
|
|
int idx = -1;
|
2023-10-16 09:53:03 +02:00
|
|
|
int p, dset, ofs, ofs_end;
|
2022-02-17 01:22:15 +01:00
|
|
|
int first, last;
|
2023-10-11 14:05:27 +02:00
|
|
|
double xx; /* the p-th sweep variable value: xctx->raw->values[sweep_idx][p] */
|
2023-09-28 00:30:00 +02:00
|
|
|
double xx0 = 0; /* first sweep value */
|
2022-02-17 01:22:15 +01:00
|
|
|
double start;
|
|
|
|
|
double end;
|
|
|
|
|
int sweepvar_wrap = 0; /* incremented on new dataset or sweep variable wrap */
|
|
|
|
|
int dataset = gr->dataset;
|
2023-10-11 14:05:27 +02:00
|
|
|
Raw *raw = xctx->raw;
|
|
|
|
|
if(!raw) {
|
|
|
|
|
dbg(0, "calc_custom_data_yrange(): no raw struct allocated\n");
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
2022-02-17 01:22:15 +01:00
|
|
|
ofs = 0;
|
|
|
|
|
start = (gr->gx1 <= gr->gx2) ? gr->gx1 : gr->gx2;
|
|
|
|
|
end = (gr->gx1 <= gr->gx2) ? gr->gx2 : gr->gx1;
|
2023-10-11 14:05:27 +02:00
|
|
|
for(dset = 0 ; dset < raw->datasets; dset++) {
|
2022-02-17 01:22:15 +01:00
|
|
|
int cnt=0, wrap;
|
2023-10-11 14:05:27 +02:00
|
|
|
register SPICE_DATA *gv = raw->values[sweep_idx];
|
2025-01-24 03:52:20 +01:00
|
|
|
register SPICE_DATA *gv0 = raw->values[0];
|
2023-10-16 09:53:03 +02:00
|
|
|
ofs_end = ofs + raw->npoints[dset];
|
2022-02-17 01:22:15 +01:00
|
|
|
first = -1;
|
|
|
|
|
last = ofs;
|
2025-01-08 13:27:07 +01:00
|
|
|
|
|
|
|
|
/* optimization: skip unwanted datasets, if no dc no need to detect sweep variable wraps */
|
|
|
|
|
if(dataset >= 0 && strcmp(xctx->raw->sim_type, "dc") && dataset != sweepvar_wrap) goto done;
|
2023-10-16 09:53:03 +02:00
|
|
|
for(p = ofs ; p < ofs_end; p++) {
|
2022-08-05 00:57:03 +02:00
|
|
|
if(gr->logx)
|
2022-08-08 01:18:42 +02:00
|
|
|
xx = mylog10(gv[p]);
|
2022-08-05 00:57:03 +02:00
|
|
|
else
|
|
|
|
|
xx = gv[p];
|
2023-06-28 08:20:09 +02:00
|
|
|
|
2025-01-24 03:52:20 +01:00
|
|
|
if(p == ofs) xx0 = gv0[p];
|
2025-11-01 22:43:48 +01:00
|
|
|
wrap = xctx->raw->sim_type && !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0;
|
2022-02-17 01:22:15 +01:00
|
|
|
if(first != -1) { /* there is something to plot ... */
|
|
|
|
|
if(xx > end || xx < start || /* ... and we ran out of graph area ... */
|
|
|
|
|
wrap) { /* ... or sweep variable changed direction */
|
|
|
|
|
if(dataset == -1 || dataset == sweepvar_wrap) {
|
2024-02-23 12:46:03 +01:00
|
|
|
idx = plot_raw_custom_data(sweep_idx, first, last, express, NULL);
|
2022-02-17 01:22:15 +01:00
|
|
|
}
|
|
|
|
|
first = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(wrap) {
|
|
|
|
|
sweepvar_wrap++;
|
|
|
|
|
cnt = 0;
|
|
|
|
|
}
|
|
|
|
|
if(xx >= start && xx <= end) {
|
|
|
|
|
if(first == -1) first = p;
|
|
|
|
|
last = p;
|
2023-02-18 09:44:11 +01:00
|
|
|
++cnt;
|
2022-02-17 01:22:15 +01:00
|
|
|
} /* if(xx >= start && xx <= end) */
|
2023-10-11 14:05:27 +02:00
|
|
|
} /* for(p = ofs ; p < ofs + raw->npoints[dset]; p++) */
|
2022-02-17 01:22:15 +01:00
|
|
|
if(first != -1) {
|
|
|
|
|
if(dataset == -1 || dataset == sweepvar_wrap) {
|
2024-02-23 12:46:03 +01:00
|
|
|
idx = plot_raw_custom_data(sweep_idx, first, last, express, NULL);
|
2022-02-17 01:22:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-08 13:27:07 +01:00
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
|
2022-02-17 01:22:15 +01:00
|
|
|
/* offset pointing to next dataset */
|
2023-10-16 09:53:03 +02:00
|
|
|
ofs = ofs_end;
|
2022-02-17 01:22:15 +01:00
|
|
|
sweepvar_wrap++;
|
|
|
|
|
} /* for(dset...) */
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 17:11:50 +02:00
|
|
|
int find_closest_wave(int i, Graph_ctx *gr)
|
|
|
|
|
{
|
|
|
|
|
double xval, yval;
|
|
|
|
|
char *node = NULL, *sweep = NULL;
|
|
|
|
|
int sweep_idx = 0;
|
|
|
|
|
char *saven, *saves, *nptr, *sptr;
|
|
|
|
|
const char *ntok, *stok;
|
|
|
|
|
int wcnt = 0, idx, expression;
|
|
|
|
|
char *express = NULL;
|
|
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
2024-06-23 15:59:22 +02:00
|
|
|
int autoload = 0, closest_dataset = -1;
|
2022-09-02 17:11:50 +02:00
|
|
|
double min=-1.0;
|
2023-10-17 00:42:31 +02:00
|
|
|
Raw *raw = NULL;
|
|
|
|
|
char *custom_rawfile = NULL; /* "rawfile" attr. set in graph: load and switch to specified raw */
|
|
|
|
|
char *sim_type = NULL;
|
2024-09-06 16:57:13 +02:00
|
|
|
const char *ptr;
|
2022-09-02 17:11:50 +02:00
|
|
|
|
2023-10-17 00:42:31 +02:00
|
|
|
if(!xctx->raw) {
|
2023-10-11 14:05:27 +02:00
|
|
|
dbg(0, "find_closest_wave(): no raw struct allocated\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if(gr->digital) return -1;
|
2024-06-23 15:59:22 +02:00
|
|
|
|
2025-04-14 12:31:59 +02:00
|
|
|
autoload = !strboolcmp(get_tok_value(r->prop_ptr,"autoload", 0), "true");
|
2024-06-23 15:59:22 +02:00
|
|
|
if(autoload == 0) autoload = 2;
|
2022-09-02 17:11:50 +02:00
|
|
|
yval = G_Y(xctx->mousey);
|
|
|
|
|
xval = G_X(xctx->mousex);
|
|
|
|
|
/* get data to plot */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node, get_tok_value(r->prop_ptr,"node", 0));
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &sweep, get_tok_value(r->prop_ptr,"sweep", 0));
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2024-11-25 14:29:19 +01:00
|
|
|
ptr = get_tok_value(r->prop_ptr,"rawfile", 0);
|
2024-09-06 16:57:13 +02:00
|
|
|
if(!ptr[0]) {
|
2025-04-18 02:02:15 +02:00
|
|
|
if(raw && raw->rawfile) my_strdup2(_ALLOC_ID_, &custom_rawfile, raw->rawfile);
|
|
|
|
|
else my_strdup2(_ALLOC_ID_, &custom_rawfile, "");
|
2024-09-06 16:57:13 +02:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &custom_rawfile, ptr);
|
2024-09-06 16:57:13 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &sim_type, get_tok_value(r->prop_ptr,"sim_type", 0));
|
2023-10-17 00:42:31 +02:00
|
|
|
if(sch_waves_loaded()!= -1 && custom_rawfile[0]) {
|
2024-06-23 15:59:22 +02:00
|
|
|
extra_rawfile(autoload, custom_rawfile, sim_type[0] ? sim_type : xctx->raw->sim_type, -1.0, -1.0);
|
2023-10-17 00:42:31 +02:00
|
|
|
}
|
|
|
|
|
raw = xctx->raw;
|
|
|
|
|
|
2022-09-02 17:11:50 +02:00
|
|
|
nptr = node;
|
|
|
|
|
sptr = sweep;
|
|
|
|
|
/* process each node given in "node" attribute, get also associated sweep var if any*/
|
2024-12-05 02:54:08 +01:00
|
|
|
while( (ntok = my_strtok_r(nptr, "\n", "\"", 0, &saven)) ) {
|
2022-09-02 17:11:50 +02:00
|
|
|
if(strstr(ntok, ",")) {
|
2023-10-11 14:05:27 +02:00
|
|
|
if(find_nth(ntok, ";,", "\"", 0, 2)[0]) continue; /* bus signal: skip */
|
2022-09-02 17:11:50 +02:00
|
|
|
}
|
2023-10-11 14:05:27 +02:00
|
|
|
stok = my_strtok_r(sptr, "\t\n ", "\"", 0, &saves);
|
2022-09-02 17:11:50 +02:00
|
|
|
nptr = sptr = NULL;
|
|
|
|
|
dbg(1, "ntok=%s\n", ntok);
|
|
|
|
|
if(stok && stok[0]) {
|
2024-02-25 12:21:13 +01:00
|
|
|
sweep_idx = get_raw_index(stok, NULL);
|
2022-09-02 17:11:50 +02:00
|
|
|
if( sweep_idx == -1) {
|
|
|
|
|
sweep_idx = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* if ntok following possible 'alias;' definition contains spaces --> custom data plot */
|
|
|
|
|
idx = -1;
|
|
|
|
|
expression = 0;
|
2023-10-11 14:05:27 +02:00
|
|
|
if(raw->values) {
|
2022-09-02 17:11:50 +02:00
|
|
|
if(strstr(ntok, ";")) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &express, find_nth(ntok, ";", "\"", 0, 2));
|
2022-09-02 17:11:50 +02:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &express, ntok);
|
2022-09-02 17:11:50 +02:00
|
|
|
}
|
2022-09-23 02:18:51 +02:00
|
|
|
if(strpbrk(express, " \n\t")) {
|
2022-09-02 17:11:50 +02:00
|
|
|
expression = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-11 14:05:27 +02:00
|
|
|
if(expression) idx = raw->nvars;
|
2024-02-25 12:21:13 +01:00
|
|
|
else idx = get_raw_index(express, NULL);
|
2023-02-03 03:35:55 +01:00
|
|
|
dbg(1, "find_closest_wave(): expression=%d, idx=%d\n", expression, idx);
|
2022-09-02 17:11:50 +02:00
|
|
|
if( idx != -1 ) {
|
2023-10-16 09:53:03 +02:00
|
|
|
int p, dset, ofs, ofs_end;
|
2022-09-02 17:11:50 +02:00
|
|
|
int first, last;
|
|
|
|
|
double xx, yy ; /* the p-th point */
|
2023-09-28 00:30:00 +02:00
|
|
|
double xx0 = 0.0; /* first sweep value */
|
2022-09-02 17:11:50 +02:00
|
|
|
double start;
|
|
|
|
|
double end;
|
|
|
|
|
int sweepvar_wrap = 0; /* incremented on new dataset or sweep variable wrap */
|
|
|
|
|
ofs = 0;
|
|
|
|
|
start = (gr->gx1 <= gr->gx2) ? gr->gx1 : gr->gx2;
|
|
|
|
|
end = (gr->gx1 <= gr->gx2) ? gr->gx2 : gr->gx1;
|
|
|
|
|
/* loop through all datasets found in raw file */
|
2023-10-11 14:05:27 +02:00
|
|
|
for(dset = 0 ; dset < raw->datasets; dset++) {
|
2023-09-28 00:30:00 +02:00
|
|
|
double prev_x = 0.0;
|
2022-09-02 17:11:50 +02:00
|
|
|
int cnt=0, wrap;
|
2023-10-11 14:05:27 +02:00
|
|
|
register SPICE_DATA *gvx = raw->values[sweep_idx];
|
2025-01-24 03:52:20 +01:00
|
|
|
register SPICE_DATA *gv0 = raw->values[0];
|
2023-02-03 03:35:55 +01:00
|
|
|
register SPICE_DATA *gvy;
|
2023-10-16 09:53:03 +02:00
|
|
|
ofs_end = ofs + raw->npoints[dset];
|
2024-02-23 12:46:03 +01:00
|
|
|
if(expression) plot_raw_custom_data(sweep_idx, ofs, ofs_end - 1, express, NULL);
|
2023-10-11 14:05:27 +02:00
|
|
|
gvy = raw->values[idx];
|
2022-09-13 13:39:25 +02:00
|
|
|
dbg(1, "find_closest_wave(): dset=%d\n", dset);
|
2022-09-02 17:11:50 +02:00
|
|
|
first = -1;
|
|
|
|
|
/* Process "npoints" simulation items
|
|
|
|
|
* p loop split repeated 2 timed (for x and y points) to preserve cache locality */
|
|
|
|
|
last = ofs;
|
2022-09-13 13:39:25 +02:00
|
|
|
dbg(1, "find_closest_wave(): xval=%g yval=%g\n", xval, yval);
|
2023-10-16 09:53:03 +02:00
|
|
|
for(p = ofs ; p < ofs_end; p++) {
|
2022-09-02 17:11:50 +02:00
|
|
|
if(gr->logx) xx = mylog10(gvx[p]);
|
2022-09-13 13:39:25 +02:00
|
|
|
else xx = gvx[p];
|
2022-09-02 17:11:50 +02:00
|
|
|
if(gr->logy) yy = mylog10(gvy[p]);
|
|
|
|
|
else yy = gvy[p];
|
2025-01-24 03:52:20 +01:00
|
|
|
if(p == ofs) xx0 = gv0[p];
|
2025-11-01 22:43:48 +01:00
|
|
|
wrap = xctx->raw->sim_type && !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0;
|
2022-09-02 17:11:50 +02:00
|
|
|
if(first != -1) {
|
|
|
|
|
if(xx > end || xx < start || wrap) {
|
|
|
|
|
dbg(1, "find_closest_wave(): last=%d\n", last);
|
|
|
|
|
first = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(wrap) {
|
|
|
|
|
cnt = 0;
|
|
|
|
|
sweepvar_wrap++;
|
|
|
|
|
}
|
|
|
|
|
if(xx >= start && xx <= end) {
|
|
|
|
|
if(first == -1) first = p;
|
2023-09-28 00:30:00 +02:00
|
|
|
if( p > ofs && XSIGN(xval - xx) != XSIGN(xval - prev_x)) {
|
2022-09-02 17:11:50 +02:00
|
|
|
|
|
|
|
|
if(min < 0.0) {
|
|
|
|
|
min = fabs(yval - yy);
|
|
|
|
|
closest_dataset = sweepvar_wrap;
|
|
|
|
|
} else {
|
|
|
|
|
double tmp = fabs(yval - yy);
|
|
|
|
|
if(tmp < min) {
|
|
|
|
|
min = tmp;
|
|
|
|
|
closest_dataset = sweepvar_wrap;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-04 01:09:05 +02:00
|
|
|
dbg(1, "find_closest_wave(): xval=%g yval=%g xx=%g yy=%g sweepvar_wrap=%d ntok=%s stok=%s\n",
|
|
|
|
|
xval, yval, xx, yy, sweepvar_wrap, ntok, stok? stok : "<NULL>");
|
2022-09-02 17:11:50 +02:00
|
|
|
}
|
|
|
|
|
last = p;
|
2023-02-18 09:44:11 +01:00
|
|
|
++cnt;
|
2022-09-02 17:11:50 +02:00
|
|
|
} /* if(xx >= start && xx <= end) */
|
|
|
|
|
prev_x = xx;
|
2023-10-11 14:05:27 +02:00
|
|
|
} /* for(p = ofs ; p < ofs + raw->npoints[dset]; p++) */
|
2022-09-02 17:11:50 +02:00
|
|
|
/* offset pointing to next dataset */
|
2023-10-16 09:53:03 +02:00
|
|
|
ofs = ofs_end;
|
2022-09-02 17:11:50 +02:00
|
|
|
sweepvar_wrap++;
|
|
|
|
|
} /* for(dset...) */
|
|
|
|
|
|
2024-02-25 12:21:13 +01:00
|
|
|
} /* if( (idx = get_raw_index(ntok, NULL)) != -1 ) */
|
2023-02-18 09:44:11 +01:00
|
|
|
++wcnt;
|
2023-10-11 14:05:27 +02:00
|
|
|
} /* while( (ntok = my_strtok_r(nptr, "\n\t ", "", 0, &saven)) ) */
|
2022-09-02 17:11:50 +02:00
|
|
|
dbg(0, "closest dataset=%d\n", closest_dataset);
|
2025-04-18 02:02:15 +02:00
|
|
|
if(express) my_free(_ALLOC_ID_, &express);
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2023-12-31 20:33:08 +01:00
|
|
|
if(sch_waves_loaded()!= -1 && custom_rawfile[0]) extra_rawfile(5, NULL, NULL, -1.0, -1.0);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &custom_rawfile);
|
|
|
|
|
my_free(_ALLOC_ID_, &sim_type);
|
2023-10-17 00:42:31 +02:00
|
|
|
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &node);
|
|
|
|
|
my_free(_ALLOC_ID_, &sweep);
|
2022-09-02 17:11:50 +02:00
|
|
|
return closest_dataset;
|
|
|
|
|
}
|
2022-01-27 20:47:27 +01:00
|
|
|
|
|
|
|
|
|
2022-01-05 23:07:08 +01:00
|
|
|
/* flags:
|
2025-01-06 13:14:12 +01:00
|
|
|
* 1: do final XCopyArea (copy 2nd buffer areas to screen)
|
|
|
|
|
* If draw_graph_all() is called from draw() no need to do XCopyArea, as draw() does it already.
|
|
|
|
|
* This makes drawing faster and removes a 'tearing' effect when moving around.
|
|
|
|
|
* 2: draw x-cursor1
|
|
|
|
|
* 4: draw x-cursor2
|
|
|
|
|
* 128: draw y-cursor1
|
|
|
|
|
* 256: draw y-cursor2
|
|
|
|
|
* 8: all drawing, if not set do only XCopyArea / x-cursor if specified
|
2022-10-13 17:36:42 +02:00
|
|
|
* ct is a pointer used in windows for cairo
|
2022-01-05 23:07:08 +01:00
|
|
|
*/
|
2025-02-28 18:04:56 +01:00
|
|
|
void draw_graph(int i, int flags, Graph_ctx *gr, void *ct)
|
2022-01-05 23:07:08 +01:00
|
|
|
{
|
2023-10-16 11:16:12 +02:00
|
|
|
int wc = 4, wave_color = 4;
|
2022-01-05 23:07:08 +01:00
|
|
|
char *node = NULL, *color = NULL, *sweep = NULL;
|
|
|
|
|
int sweep_idx = 0;
|
|
|
|
|
int n_nodes; /* number of variables to display in a single graph */
|
|
|
|
|
char *saven, *savec, *saves, *nptr, *cptr, *sptr;
|
|
|
|
|
const char *ntok, *ctok, *stok;
|
|
|
|
|
char *bus_msb = NULL;
|
2022-02-14 19:28:24 +01:00
|
|
|
int wcnt = 0, idx, expression;
|
2024-04-01 23:33:15 +02:00
|
|
|
int measure_p;
|
|
|
|
|
double measure_x;
|
|
|
|
|
double measure_prev_x;
|
2022-02-14 17:51:18 +01:00
|
|
|
char *express = NULL;
|
2022-01-05 23:07:08 +01:00
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
2023-10-13 15:51:51 +02:00
|
|
|
int node_dataset = -1; /* dataset specified as %<n> after node/bus/expression name */
|
|
|
|
|
char *ntok_copy = NULL; /* copy of ntok without %<n> */
|
2023-10-17 00:42:31 +02:00
|
|
|
char *custom_rawfile = NULL; /* "rawfile" attr. set in graph: load and switch to specified raw */
|
|
|
|
|
char *sim_type = NULL;
|
2023-11-15 19:18:28 +01:00
|
|
|
int save_extra_idx = -1;
|
2024-09-05 00:51:15 +02:00
|
|
|
double cursor1, cursor2;
|
|
|
|
|
|
2022-09-20 17:59:01 +02:00
|
|
|
if(xctx->only_probes) return;
|
2022-01-05 23:07:08 +01:00
|
|
|
if(RECT_OUTSIDE( gr->sx1, gr->sy1, gr->sx2, gr->sy2,
|
|
|
|
|
xctx->areax1, xctx->areay1, xctx->areax2, xctx->areay2)) return;
|
2022-09-18 11:06:48 +02:00
|
|
|
|
2025-02-28 19:19:06 +01:00
|
|
|
if(r->flags & 4) { /* private_cursor */
|
2024-09-05 00:51:15 +02:00
|
|
|
const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0);
|
|
|
|
|
if(s[0]) {
|
2025-01-10 01:52:54 +01:00
|
|
|
cursor1 = atof_eng(s);
|
2024-09-05 00:51:15 +02:00
|
|
|
} else {
|
|
|
|
|
cursor1 = xctx->graph_cursor1_x;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cursor1 = xctx->graph_cursor1_x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(r->flags & 4) { /* private_cursor */
|
|
|
|
|
const char *s = get_tok_value(r->prop_ptr, "cursor2_x", 0);
|
|
|
|
|
if(s[0]) {
|
2025-01-10 01:52:54 +01:00
|
|
|
cursor2 = atof_eng(s);
|
2024-09-05 00:51:15 +02:00
|
|
|
} else {
|
|
|
|
|
cursor2 = xctx->graph_cursor2_x;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cursor2 = xctx->graph_cursor2_x;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 14:05:27 +02:00
|
|
|
#if 0
|
|
|
|
|
dbg(0, "draw_graph(): window: %d %d %d %d\n", xctx->areax1, xctx->areay1, xctx->areax2, xctx->areay2);
|
|
|
|
|
dbg(0, "draw_graph(): graph: %g %g %g %g\n", gr->sx1, gr->sy1, gr->sx2, gr->sy2);
|
2025-01-07 04:20:32 +01:00
|
|
|
dbg(0, "draw_graph(): i = %d, flags = %d graph_flags=%d\n", i, flags, xctx->graph_flags);
|
2023-10-11 14:05:27 +02:00
|
|
|
#endif
|
2022-09-18 11:06:48 +02:00
|
|
|
|
2021-12-30 15:45:38 +01:00
|
|
|
/* draw stuff */
|
|
|
|
|
if(flags & 8) {
|
2024-06-23 15:59:22 +02:00
|
|
|
int autoload = 0;
|
2023-11-15 19:18:28 +01:00
|
|
|
char *tmp_ptr = NULL;
|
2023-10-17 14:00:43 +02:00
|
|
|
int save_datasets = -1, save_npoints = -1;
|
2024-09-06 16:57:13 +02:00
|
|
|
const char *ptr;
|
2023-10-11 14:05:27 +02:00
|
|
|
#if !defined(__unix__) && HAS_CAIRO==1
|
2022-03-01 19:36:08 +01:00
|
|
|
double sw = (gr->sx2 - gr->sx1);
|
|
|
|
|
double sh = (gr->sy2 - gr->sy1);
|
|
|
|
|
clear_cairo_surface(xctx->cairo_save_ctx, gr->sx1, gr->sy1, sw, sh);
|
|
|
|
|
clear_cairo_surface(xctx->cairo_ctx, gr->sx1, gr->sy1, sw, sh);
|
2023-10-11 14:05:27 +02:00
|
|
|
#endif
|
2025-04-14 12:31:59 +02:00
|
|
|
autoload = !strboolcmp(get_tok_value(r->prop_ptr,"autoload", 0), "true");
|
2025-01-17 17:15:48 +01:00
|
|
|
if(autoload == 0) autoload = 2; /* 2: switch */
|
|
|
|
|
else if(autoload == 1) autoload = 33; /* 1: read, 32: no_warning */
|
2021-12-30 15:45:38 +01:00
|
|
|
/* graph box, gridlines and axes */
|
2022-09-25 21:11:52 +02:00
|
|
|
draw_graph_grid(gr, ct);
|
2021-12-30 15:45:38 +01:00
|
|
|
/* get data to plot */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node, get_tok_value(r->prop_ptr,"node", 0));
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &color, get_tok_value(r->prop_ptr,"color", 0));
|
|
|
|
|
my_strdup2(_ALLOC_ID_, &sweep, get_tok_value(r->prop_ptr,"sweep", 0));
|
2024-09-06 16:57:13 +02:00
|
|
|
|
2024-11-25 14:29:19 +01:00
|
|
|
ptr = get_tok_value(r->prop_ptr,"rawfile", 0);
|
2024-09-06 16:57:13 +02:00
|
|
|
if(!ptr[0]) {
|
2025-04-18 02:02:15 +02:00
|
|
|
if(xctx->raw && xctx->raw->rawfile) my_strdup2(_ALLOC_ID_, &custom_rawfile, xctx->raw->rawfile);
|
|
|
|
|
else my_strdup2(_ALLOC_ID_, &custom_rawfile, "");
|
2024-09-06 16:57:13 +02:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &custom_rawfile, ptr);
|
2024-09-06 16:57:13 +02:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &sim_type, get_tok_value(r->prop_ptr,"sim_type", 0));
|
2024-09-06 16:57:13 +02:00
|
|
|
dbg(1, "draw_graph(): graph %d: custom_rawfile=%s autoload=%d sim_type=%s\n",
|
|
|
|
|
i, custom_rawfile, autoload, sim_type);
|
2023-11-15 19:18:28 +01:00
|
|
|
save_extra_idx = xctx->extra_idx;
|
2023-10-17 14:00:43 +02:00
|
|
|
|
2021-12-30 15:45:38 +01:00
|
|
|
nptr = node;
|
|
|
|
|
cptr = color;
|
|
|
|
|
sptr = sweep;
|
2024-12-05 02:54:08 +01:00
|
|
|
n_nodes = count_items(node, "\n", "\"");
|
2024-04-01 23:23:45 +02:00
|
|
|
|
2021-12-30 15:45:38 +01:00
|
|
|
/* process each node given in "node" attribute, get also associated color/sweep var if any*/
|
2024-12-05 02:54:08 +01:00
|
|
|
while( (ntok = my_strtok_r(nptr, "\n", "\"", 4, &saven)) ) {
|
2024-09-09 13:20:02 +02:00
|
|
|
int valid_rawfile = 1;
|
2023-11-15 19:18:28 +01:00
|
|
|
char *nd = NULL;
|
|
|
|
|
char str_extra_idx[30];
|
2023-11-07 01:05:45 +01:00
|
|
|
|
2024-06-23 15:59:22 +02:00
|
|
|
nptr = NULL;
|
2024-04-01 23:33:15 +02:00
|
|
|
measure_p = -1;
|
|
|
|
|
measure_x = 0.0;
|
|
|
|
|
measure_prev_x = 0.0;
|
2024-09-08 13:43:29 +02:00
|
|
|
if(custom_rawfile[0]) {
|
|
|
|
|
if(extra_rawfile(autoload, custom_rawfile, sim_type[0] ? sim_type :
|
|
|
|
|
(xctx->raw && xctx->raw->sim_type ? xctx->raw->sim_type : NULL), -1.0, -1.0) == 0) {
|
2025-01-11 22:45:42 +01:00
|
|
|
valid_rawfile = 0;
|
2024-06-23 15:59:22 +02:00
|
|
|
}
|
2023-11-15 19:18:28 +01:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &nd, find_nth(ntok, "%", "\"", 0, 2));
|
2023-10-13 23:09:27 +02:00
|
|
|
if(wcnt >= n_nodes) {
|
|
|
|
|
dbg(0, "draw_graph(): WARNING: wcnt (wave #) >= n_nodes (counted # of waves)\n");
|
|
|
|
|
dbg(0, "draw_graph(): n_nodes=%d\n", n_nodes);
|
|
|
|
|
wcnt--; /* nosense, but avoid a crash */
|
|
|
|
|
}
|
|
|
|
|
/* if %<n> is specified after node name, <n> is the dataset number to plot in graph */
|
2023-11-15 19:18:28 +01:00
|
|
|
/* if %n rawfile.raw is specified use rawfile.raw for this node */
|
|
|
|
|
|
2023-10-13 15:51:51 +02:00
|
|
|
if(nd[0]) {
|
2023-11-15 19:18:28 +01:00
|
|
|
int pos = 1;
|
2023-11-16 02:58:26 +01:00
|
|
|
if(isonlydigit(find_nth(nd, "\n ", "\"", 0, 1))) pos = 2;
|
2024-09-06 16:57:13 +02:00
|
|
|
if(xctx->raw && xctx->raw->values) {
|
2023-11-15 19:18:28 +01:00
|
|
|
char *node_rawfile = NULL;
|
|
|
|
|
char *node_sim_type = NULL;
|
2023-11-16 02:58:26 +01:00
|
|
|
tclvareval("subst {", find_nth(nd, "\n ", "\"", 0, pos), "}", NULL);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node_rawfile, tclresult());
|
2023-11-16 02:58:26 +01:00
|
|
|
tclvareval("subst {", find_nth(nd, "\n ", "\"", 0, pos + 1), "}", NULL);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &node_sim_type, tclresult()[0] ? tclresult() :
|
2023-11-15 19:18:28 +01:00
|
|
|
sim_type[0] ? sim_type : xctx->raw->sim_type);
|
|
|
|
|
dbg(1, "node_rawfile=|%s| node_sim_type=|%s|\n", node_rawfile, node_sim_type);
|
|
|
|
|
if(node_rawfile && node_rawfile[0]) {
|
2024-06-23 15:59:22 +02:00
|
|
|
if(extra_rawfile(autoload, node_rawfile, node_sim_type, -1.0, -1.0) == 0) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &node_rawfile);
|
|
|
|
|
my_free(_ALLOC_ID_, &node_sim_type);
|
2024-09-09 13:20:02 +02:00
|
|
|
valid_rawfile = 0;
|
2024-06-23 15:59:22 +02:00
|
|
|
}
|
2023-11-15 19:18:28 +01:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &node_rawfile);
|
|
|
|
|
my_free(_ALLOC_ID_, &node_sim_type);
|
2023-11-15 19:18:28 +01:00
|
|
|
}
|
2023-11-16 00:02:25 +01:00
|
|
|
if(pos == 2) node_dataset = atoi(nd);
|
2023-11-15 19:18:28 +01:00
|
|
|
else node_dataset = -1;
|
|
|
|
|
dbg(1, "nd=|%s|, node_dataset = %d\n", nd, node_dataset);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &ntok_copy, find_nth(ntok, "%", "\"", 4, 1));
|
2023-10-13 15:51:51 +02:00
|
|
|
} else {
|
|
|
|
|
node_dataset = -1;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &ntok_copy, ntok);
|
2023-10-13 15:51:51 +02:00
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
if(nd) my_free(_ALLOC_ID_, &nd);
|
2023-11-17 01:20:52 +01:00
|
|
|
/* transform multiple OP points into a dc sweep */
|
2024-09-06 16:57:13 +02:00
|
|
|
if(xctx->raw && xctx->raw->sim_type && !strcmp(xctx->raw->sim_type, "op")
|
|
|
|
|
&& xctx->raw->datasets > 1 && xctx->raw->npoints[0] == 1) {
|
|
|
|
|
save_datasets = xctx->raw->datasets;
|
|
|
|
|
xctx->raw->datasets = 1;
|
|
|
|
|
save_npoints = xctx->raw->npoints[0];
|
|
|
|
|
xctx->raw->npoints[0] = xctx->raw->allpoints;
|
2023-11-17 01:20:52 +01:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &nd);
|
2023-10-13 15:51:51 +02:00
|
|
|
dbg(1, "ntok=|%s|\nntok_copy=|%s|\nnode_dataset=%d\n", ntok, ntok_copy, node_dataset);
|
2023-11-15 19:18:28 +01:00
|
|
|
|
|
|
|
|
tmp_ptr = find_nth(ntok_copy, ";", "\"", 4, 2);
|
|
|
|
|
if(strstr(tmp_ptr, ",")) {
|
|
|
|
|
tmp_ptr = find_nth(tmp_ptr, ",", "\"", 4, 1);
|
2023-10-11 16:31:42 +02:00
|
|
|
/* also trim spaces */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &bus_msb, trim_chars(tmp_ptr, "\n "));
|
2021-12-27 05:22:19 +01:00
|
|
|
}
|
2025-01-04 11:33:58 +01:00
|
|
|
dbg(1, "ntok_copy=|%s|, bus_msb=|%s|\n", ntok_copy, bus_msb ? bus_msb : "<NULL>");
|
2023-10-11 14:05:27 +02:00
|
|
|
ctok = my_strtok_r(cptr, " ", "", 0, &savec);
|
|
|
|
|
stok = my_strtok_r(sptr, "\t\n ", "\"", 0, &saves);
|
2024-06-23 15:59:22 +02:00
|
|
|
cptr = sptr = NULL;
|
2025-01-04 11:33:58 +01:00
|
|
|
dbg(1, "ntok_copy=%s ctok=%s\n", ntok_copy, ctok? ctok: "<NULL>");
|
2022-10-13 19:33:30 +02:00
|
|
|
if(ctok && ctok[0]) wc = atoi(ctok);
|
|
|
|
|
if(wc < 0) wc = 4;
|
|
|
|
|
if(wc >= cadlayers) wc = cadlayers - 1;
|
2021-12-30 15:45:38 +01:00
|
|
|
if(stok && stok[0]) {
|
2024-02-25 12:21:13 +01:00
|
|
|
sweep_idx = get_raw_index(stok, NULL);
|
2021-12-30 15:45:38 +01:00
|
|
|
if( sweep_idx == -1) {
|
|
|
|
|
sweep_idx = 0;
|
|
|
|
|
}
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2022-10-13 19:33:30 +02:00
|
|
|
draw_graph_variables(wcnt, wc, n_nodes, sweep_idx, flags, ntok, stok, bus_msb, gr);
|
2023-10-13 15:51:51 +02:00
|
|
|
/* if ntok_copy following possible 'alias;' definition contains spaces --> custom data plot */
|
2022-02-12 04:55:02 +01:00
|
|
|
idx = -1;
|
2022-02-14 19:28:24 +01:00
|
|
|
expression = 0;
|
2024-09-09 13:20:02 +02:00
|
|
|
if(!bus_msb) {
|
2023-10-13 15:51:51 +02:00
|
|
|
if(strstr(ntok_copy, ";")) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &express, find_nth(ntok_copy, ";", "\"", 0, 2));
|
2022-02-14 17:51:18 +01:00
|
|
|
} else {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &express, ntok_copy);
|
2022-02-14 17:51:18 +01:00
|
|
|
}
|
2023-10-13 15:51:51 +02:00
|
|
|
dbg(1, "express=|%s|\n", express);
|
2022-09-23 02:18:51 +02:00
|
|
|
if(strpbrk(express, " \n\t")) {
|
2022-02-14 19:28:24 +01:00
|
|
|
expression = 1;
|
2022-02-14 17:51:18 +01:00
|
|
|
}
|
2024-03-25 22:28:29 +01:00
|
|
|
}
|
2024-09-09 13:20:02 +02:00
|
|
|
if(sch_waves_loaded() != -1 && tclgetboolvar("auto_hilight_graph_nodes")) {
|
2024-09-06 16:57:13 +02:00
|
|
|
if(!expression && xctx->raw->sim_type && strcmp(xctx->raw->sim_type, "op") ) {
|
2024-03-25 22:28:29 +01:00
|
|
|
if(!bus_msb) hilight_graph_node(express, wc);
|
|
|
|
|
else hilight_graph_node(bus_msb, wc);
|
2024-03-25 11:39:57 +01:00
|
|
|
}
|
2022-02-12 04:55:02 +01:00
|
|
|
}
|
2025-01-04 11:33:58 +01:00
|
|
|
dbg(1, "express=%s, bus_msb=%s\n", express ? express : "<NULL>", bus_msb ? bus_msb : "<NULL>");
|
2023-10-13 15:51:51 +02:00
|
|
|
/* quickly find index number of ntok_copy variable to be plotted */
|
2024-09-09 13:20:02 +02:00
|
|
|
if(sch_waves_loaded() != -1 && valid_rawfile &&
|
|
|
|
|
(expression || (idx = get_raw_index(bus_msb ? bus_msb : express, NULL)) != -1)) {
|
2023-10-16 09:53:03 +02:00
|
|
|
int p, dset, ofs, ofs_end;
|
2021-12-30 15:45:38 +01:00
|
|
|
int poly_npoints;
|
2022-01-01 04:22:23 +01:00
|
|
|
int first, last;
|
2024-09-06 16:57:13 +02:00
|
|
|
double xx; /* the p-th sweep variable value: xctx->raw->values[sweep_idx][p] */
|
2023-09-28 00:30:00 +02:00
|
|
|
double xx0 = 0.0; /* the first sweep value */
|
2021-12-30 15:45:38 +01:00
|
|
|
double start;
|
|
|
|
|
double end;
|
2022-02-17 01:22:15 +01:00
|
|
|
int n_bits = 1;
|
2024-03-30 19:20:51 +01:00
|
|
|
SPICE_DATA **idx_arr = NULL;
|
2022-01-05 17:38:01 +01:00
|
|
|
int sweepvar_wrap = 0; /* incremented on new dataset or sweep variable wrap */
|
2022-01-01 15:46:03 +01:00
|
|
|
XPoint *point = NULL;
|
2023-10-13 15:51:51 +02:00
|
|
|
int dataset = node_dataset >=0 ? node_dataset : gr->dataset;
|
2022-01-06 04:17:16 +01:00
|
|
|
int digital = gr->digital;
|
2022-01-05 23:07:08 +01:00
|
|
|
ofs = 0;
|
|
|
|
|
start = (gr->gx1 <= gr->gx2) ? gr->gx1 : gr->gx2;
|
|
|
|
|
end = (gr->gx1 <= gr->gx2) ? gr->gx2 : gr->gx1;
|
2022-01-04 05:54:25 +01:00
|
|
|
if(bus_msb) {
|
2023-10-13 15:51:51 +02:00
|
|
|
idx_arr = get_bus_idx_array(ntok_copy, &n_bits); /* idx_arr allocated by function, must free! */
|
2022-01-04 05:54:25 +01:00
|
|
|
}
|
2022-01-05 17:38:01 +01:00
|
|
|
bbox(START, 0.0, 0.0, 0.0, 0.0);
|
2022-01-05 23:07:08 +01:00
|
|
|
bbox(ADD,gr->x1, gr->y1, gr->x2, gr->y2);
|
2022-01-05 17:38:01 +01:00
|
|
|
bbox(SET, 0.0, 0.0, 0.0, 0.0);
|
2022-01-05 23:07:08 +01:00
|
|
|
/* loop through all datasets found in raw file */
|
2024-04-01 23:23:45 +02:00
|
|
|
|
2024-09-09 13:20:02 +02:00
|
|
|
if(sch_waves_loaded() != -1) for(dset = 0 ; dset < xctx->raw->datasets; dset++) {
|
2024-03-30 19:20:51 +01:00
|
|
|
double prev_x;
|
2022-02-17 01:22:15 +01:00
|
|
|
int cnt=0, wrap;
|
2024-09-06 16:57:13 +02:00
|
|
|
register SPICE_DATA *gv = xctx->raw->values[sweep_idx];
|
2025-01-24 03:52:20 +01:00
|
|
|
register SPICE_DATA *gv0 = xctx->raw->values[0];
|
2023-10-16 09:53:03 +02:00
|
|
|
|
2024-09-06 16:57:13 +02:00
|
|
|
ofs_end = ofs + xctx->raw->npoints[dset];
|
2022-02-17 01:22:15 +01:00
|
|
|
first = -1;
|
2022-01-05 17:38:01 +01:00
|
|
|
poly_npoints = 0;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_realloc(_ALLOC_ID_, &point, xctx->raw->npoints[dset] * sizeof(XPoint));
|
2022-01-05 17:38:01 +01:00
|
|
|
/* Process "npoints" simulation items
|
|
|
|
|
* p loop split repeated 2 timed (for x and y points) to preserve cache locality */
|
2024-03-30 19:20:51 +01:00
|
|
|
prev_x = 0;
|
2022-01-05 17:38:01 +01:00
|
|
|
last = ofs;
|
2025-01-08 13:27:07 +01:00
|
|
|
|
|
|
|
|
/* optimization: skip unwanted datasets, if no dc no need to detect sweep variable wraps */
|
|
|
|
|
if(dataset >= 0 && strcmp(xctx->raw->sim_type, "dc") && dataset != sweepvar_wrap) goto done;
|
2023-10-16 09:53:03 +02:00
|
|
|
for(p = ofs ; p < ofs_end; p++) {
|
2024-11-30 01:16:37 +01:00
|
|
|
double xxprevious, xxfollowing;
|
|
|
|
|
|
2022-08-08 01:18:42 +02:00
|
|
|
if(gr->logx) xx = mylog10(gv[p]);
|
2022-08-05 00:57:03 +02:00
|
|
|
else xx = gv[p];
|
2024-11-30 01:16:37 +01:00
|
|
|
|
|
|
|
|
xxprevious = xxfollowing = xx;
|
2025-01-24 03:52:20 +01:00
|
|
|
/* do not use sweep variable for wrap detection. sweep variables other that simulation sweep var
|
|
|
|
|
* are simulated and thos no equality test can be done, and any "approx equal" test si going
|
|
|
|
|
* to do unexpected things (liek in simulations with very dense steps) */
|
|
|
|
|
if(p == ofs) xx0 = gv0[p]; /* gv[p];*/
|
2025-11-01 22:43:48 +01:00
|
|
|
wrap = xctx->raw->sim_type && !strcmp(xctx->raw->sim_type, "dc") && cnt > 1 && gv0[p] == xx0;
|
2024-11-30 01:16:37 +01:00
|
|
|
#if 1 /* plot one point before start and one point after end so
|
2024-12-18 01:55:05 +01:00
|
|
|
* waves will extend to whole graph area even if there are few points
|
|
|
|
|
* but NOT if we are about to wrap (missing 1st/last point in 2-var dc sweeps) */
|
|
|
|
|
if(!wrap && p > ofs) {
|
2024-11-30 01:16:37 +01:00
|
|
|
if(gr->logx) xxprevious = mylog10(gv[p - 1]);
|
|
|
|
|
else xxprevious = gv[p - 1];
|
|
|
|
|
}
|
2024-12-18 01:55:05 +01:00
|
|
|
/* .................<-- next point will not wrap. */
|
2024-12-21 13:05:33 +01:00
|
|
|
if(p < ofs_end - 1 && gv[p + 1] != xx0) {
|
2024-11-30 01:16:37 +01:00
|
|
|
if(gr->logx) xxfollowing = mylog10(gv[p + 1]);
|
|
|
|
|
else xxfollowing = gv[p + 1];
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2025-01-27 15:33:46 +01:00
|
|
|
/* comment dbg() calls since we are in a deep, deep nested loop */
|
|
|
|
|
/* dbg(1, "draw_graph(): wrap=%d, xx=%g, xx0=%g, p=%d\n", wrap, xx, xx0, p); */
|
|
|
|
|
|
2024-12-10 00:07:33 +01:00
|
|
|
/* if gr->mode == 2 (HistH) don't wrap */
|
2024-12-18 01:55:05 +01:00
|
|
|
if((gr->mode != 2) && first != -1) { /* there is something to plot ... */
|
|
|
|
|
/* ... and we ran out of graph area ... */
|
|
|
|
|
/* ... or sweep variable changed direction */
|
|
|
|
|
if(xxprevious > end || xxfollowing < start || wrap) {
|
2022-01-06 03:49:18 +01:00
|
|
|
if(dataset == -1 || dataset == sweepvar_wrap) {
|
2022-01-05 23:07:08 +01:00
|
|
|
/* plot graph */
|
2023-05-27 23:36:10 +02:00
|
|
|
if(gr->rainbow) wave_color = 4 + (wc - 4 + sweepvar_wrap) % (cadlayers - 4);
|
|
|
|
|
else wave_color = wc;
|
2022-01-04 05:54:25 +01:00
|
|
|
if(bus_msb) {
|
2022-01-06 03:49:18 +01:00
|
|
|
if(digital) {
|
2024-11-30 01:16:37 +01:00
|
|
|
draw_graph_bus_points(ntok_copy, n_bits, idx_arr, first, last, wave_color,
|
|
|
|
|
sweep_idx, wcnt, n_nodes, gr, ct);
|
2021-12-30 15:45:38 +01:00
|
|
|
}
|
2022-01-04 05:54:25 +01:00
|
|
|
} else {
|
2024-02-23 12:46:03 +01:00
|
|
|
if(expression) idx = plot_raw_custom_data(sweep_idx, first, last, express, NULL);
|
2022-09-25 21:11:52 +02:00
|
|
|
draw_graph_points(idx, first, last, point, wave_color, wcnt, n_nodes, gr, ct);
|
2022-01-04 05:54:25 +01:00
|
|
|
}
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
2022-01-05 17:38:01 +01:00
|
|
|
poly_npoints = 0;
|
|
|
|
|
first = -1;
|
2021-12-25 05:15:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-05 17:38:01 +01:00
|
|
|
if(wrap) {
|
|
|
|
|
sweepvar_wrap++;
|
|
|
|
|
cnt = 0;
|
|
|
|
|
}
|
2024-12-10 00:07:33 +01:00
|
|
|
/* for HistH get all points */
|
|
|
|
|
if((gr->mode == 2) || (xxfollowing >= start && xxprevious <= end)) {
|
2022-01-05 17:38:01 +01:00
|
|
|
if(first == -1) first = p;
|
|
|
|
|
/* Build poly x array. Translate from graph coordinates to screen coords */
|
2025-03-08 10:53:21 +01:00
|
|
|
point[poly_npoints].x = (short)CLIP(S_X(xx), -30000, 30000);
|
2024-03-30 19:20:51 +01:00
|
|
|
if(dataset == -1 || dataset == sweepvar_wrap) {
|
|
|
|
|
/* cursor1: show measurements on nodes in graph */
|
2024-04-01 23:33:15 +02:00
|
|
|
if(flags & 2 && measure_p == -1 && cnt) {
|
2024-09-05 00:51:15 +02:00
|
|
|
double curs1;
|
|
|
|
|
|
|
|
|
|
curs1 = cursor1;
|
|
|
|
|
if(gr->logx) curs1 = mylog10(cursor1);
|
|
|
|
|
if(XSIGN(xx - curs1) != XSIGN(prev_x - curs1)) {
|
2024-04-01 23:33:15 +02:00
|
|
|
measure_p = p;
|
|
|
|
|
measure_x = xx;
|
|
|
|
|
measure_prev_x = prev_x;
|
2024-03-30 19:20:51 +01:00
|
|
|
}
|
2024-04-01 23:33:15 +02:00
|
|
|
} /* if(flags & 2 && measure_p == -1 && cnt) */
|
2024-03-30 19:20:51 +01:00
|
|
|
} /* if(dataset == -1 || dataset == sweepvar_wrap) */
|
2022-01-05 17:38:01 +01:00
|
|
|
last = p;
|
|
|
|
|
poly_npoints++;
|
2023-02-18 09:44:11 +01:00
|
|
|
++cnt;
|
2022-01-05 17:38:01 +01:00
|
|
|
} /* if(xx >= start && xx <= end) */
|
2024-03-30 19:20:51 +01:00
|
|
|
prev_x = xx;
|
2024-09-06 16:57:13 +02:00
|
|
|
} /* for(p = ofs ; p < ofs + xctx->raw->npoints[dset]; p++) */
|
2025-01-08 13:27:07 +01:00
|
|
|
|
|
|
|
|
|
2022-01-05 17:38:01 +01:00
|
|
|
if(first != -1) {
|
2022-01-06 03:49:18 +01:00
|
|
|
if(dataset == -1 || dataset == sweepvar_wrap) {
|
2022-01-26 15:20:00 +01:00
|
|
|
/* plot graph. Bus bundles are not plotted if graph is not digital.*/
|
2023-05-27 23:36:10 +02:00
|
|
|
if(gr->rainbow) wave_color = 4 + (wc - 4 + sweepvar_wrap) % (cadlayers - 4);
|
|
|
|
|
else wave_color = wc;
|
2021-12-30 15:45:38 +01:00
|
|
|
if(bus_msb) {
|
2022-01-06 03:49:18 +01:00
|
|
|
if(digital) {
|
2023-10-13 15:51:51 +02:00
|
|
|
draw_graph_bus_points(ntok_copy, n_bits, idx_arr, first, last, wave_color,
|
2022-09-25 21:11:52 +02:00
|
|
|
sweep_idx, wcnt, n_nodes, gr, ct);
|
2021-12-30 15:45:38 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
2024-02-23 12:46:03 +01:00
|
|
|
if(expression) idx = plot_raw_custom_data(sweep_idx, first, last, express, NULL);
|
2022-09-25 21:11:52 +02:00
|
|
|
draw_graph_points(idx, first, last, point, wave_color, wcnt, n_nodes, gr, ct);
|
2021-12-29 05:11:39 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-05 17:38:01 +01:00
|
|
|
}
|
2025-01-08 13:27:07 +01:00
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
|
2021-12-30 15:45:38 +01:00
|
|
|
/* offset pointing to next dataset */
|
2023-10-16 09:53:03 +02:00
|
|
|
ofs = ofs_end;
|
2022-01-05 17:38:01 +01:00
|
|
|
sweepvar_wrap++;
|
2021-12-30 15:45:38 +01:00
|
|
|
} /* for(dset...) */
|
2022-01-05 17:38:01 +01:00
|
|
|
bbox(END, 0.0, 0.0, 0.0, 0.0);
|
2024-09-09 13:20:02 +02:00
|
|
|
if(sch_waves_loaded()!= -1 && flags & 2 && measure_p != -1)
|
2024-04-01 23:33:15 +02:00
|
|
|
show_node_measures(measure_p, measure_x, measure_prev_x, bus_msb, wave_color,
|
2024-09-05 00:51:15 +02:00
|
|
|
idx, idx_arr, n_bits, n_nodes, ntok_copy, wcnt, gr, r, cursor1);
|
2024-03-30 19:20:51 +01:00
|
|
|
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &point);
|
|
|
|
|
if(idx_arr) my_free(_ALLOC_ID_, &idx_arr);
|
2024-02-25 12:21:13 +01:00
|
|
|
} /* if( expression || (idx = get_raw_index(bus_msb ? bus_msb : express, NULL)) != -1 ) */
|
2023-02-18 09:44:11 +01:00
|
|
|
++wcnt;
|
2025-04-18 02:02:15 +02:00
|
|
|
if(bus_msb) my_free(_ALLOC_ID_, &bus_msb);
|
2024-09-09 13:20:02 +02:00
|
|
|
if(sch_waves_loaded()!= -1 && save_npoints != -1) { /* restore multiple OP points from artificial dc sweep */
|
2024-09-06 16:57:13 +02:00
|
|
|
xctx->raw->datasets = save_datasets;
|
|
|
|
|
xctx->raw->npoints[0] = save_npoints;
|
2023-11-17 01:20:52 +01:00
|
|
|
}
|
2024-02-23 04:19:29 +01:00
|
|
|
if(save_extra_idx != -1 && save_extra_idx != xctx->extra_idx) {
|
2023-11-15 19:18:28 +01:00
|
|
|
my_snprintf(str_extra_idx, S(str_extra_idx), "%d", save_extra_idx);
|
2023-12-31 20:33:08 +01:00
|
|
|
extra_rawfile(2, str_extra_idx, NULL, -1.0, -1.0);
|
2023-11-15 19:18:28 +01:00
|
|
|
}
|
|
|
|
|
|
2023-10-11 14:05:27 +02:00
|
|
|
} /* while( (ntok = my_strtok_r(nptr, "\n\t ", "", 0, &saven)) ) */
|
2025-04-18 02:02:15 +02:00
|
|
|
if(ntok_copy) my_free(_ALLOC_ID_, &ntok_copy);
|
|
|
|
|
if(express) my_free(_ALLOC_ID_, &express);
|
2023-12-31 20:33:08 +01:00
|
|
|
/* if(sch_waves_loaded()!= -1 && custom_rawfile[0]) extra_rawfile(5, NULL, NULL, -1.0, -1.0); */
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &custom_rawfile);
|
|
|
|
|
my_free(_ALLOC_ID_, &sim_type);
|
|
|
|
|
my_free(_ALLOC_ID_, &node);
|
|
|
|
|
my_free(_ALLOC_ID_, &color);
|
|
|
|
|
my_free(_ALLOC_ID_, &sweep);
|
2024-03-30 19:20:51 +01:00
|
|
|
} /* if(flags & 8) */
|
|
|
|
|
/*
|
|
|
|
|
* bbox(START, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
* bbox(ADD, gr->rx1, gr->ry1, gr->rx2, gr->ry2);
|
|
|
|
|
* bbox(SET_INSIDE, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
*/
|
|
|
|
|
if(flags & 8) {
|
2022-01-06 05:34:42 +01:00
|
|
|
/* cursor1 */
|
2025-01-06 13:14:12 +01:00
|
|
|
if((flags & 2)) draw_cursor(cursor1, cursor2, 1, gr);
|
2022-01-06 05:34:42 +01:00
|
|
|
/* cursor2 */
|
2025-01-06 13:14:12 +01:00
|
|
|
if((flags & 4)) draw_cursor(cursor2, cursor1, 3, gr);
|
2022-01-06 05:34:42 +01:00
|
|
|
/* difference between cursors */
|
2025-01-06 13:14:12 +01:00
|
|
|
if((flags & 2) && (flags & 4)) draw_cursor_difference(cursor1, cursor2, gr);
|
2025-01-07 04:20:32 +01:00
|
|
|
/* difference between hcursors */
|
|
|
|
|
if((flags & 128) && (flags & 256)) draw_hcursor_difference(gr->hcursor1_y, gr->hcursor2_y, gr);
|
2025-01-06 13:14:12 +01:00
|
|
|
/* hcursor1 */
|
2025-01-07 04:20:32 +01:00
|
|
|
if(flags & 128) draw_hcursor(gr->hcursor1_y, 15, gr);
|
2025-01-06 13:14:12 +01:00
|
|
|
/* hcursor2 */
|
2025-01-07 04:20:32 +01:00
|
|
|
if(flags & 256) draw_hcursor(gr->hcursor2_y, 19, gr);
|
2024-03-30 19:20:51 +01:00
|
|
|
}
|
2021-12-30 23:22:13 +01:00
|
|
|
if(flags & 1) { /* copy save buffer to screen */
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
if(!xctx->draw_window) {
|
2023-10-30 18:03:52 +01:00
|
|
|
/*
|
|
|
|
|
* MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], xctx->xrect[0].x, xctx->xrect[0].y,
|
|
|
|
|
* xctx->xrect[0].width, xctx->xrect[0].height, xctx->xrect[0].x, xctx->xrect[0].y);
|
|
|
|
|
*/
|
|
|
|
|
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
|
|
|
|
|
gr->rx1, gr->ry1, gr->rx2, gr->ry2, gr->rx1, gr->ry1, 0.0);
|
|
|
|
|
|
2021-12-22 18:25:15 +01:00
|
|
|
}
|
2021-12-22 04:39:23 +01:00
|
|
|
}
|
2024-03-30 19:20:51 +01:00
|
|
|
/* bbox(END, 0.0, 0.0, 0.0, 0.0); */
|
2021-12-22 04:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-23 11:59:39 +01:00
|
|
|
/* flags:
|
2021-12-30 15:45:38 +01:00
|
|
|
* see draw_graph()
|
2021-12-23 11:59:39 +01:00
|
|
|
*/
|
2022-02-19 14:31:55 +01:00
|
|
|
static void draw_graph_all(int flags)
|
2021-12-22 04:39:23 +01:00
|
|
|
{
|
2022-01-05 23:07:08 +01:00
|
|
|
int i, sch_loaded, hide_graphs;
|
2021-12-22 04:39:23 +01:00
|
|
|
int bbox_set = 0;
|
2023-09-28 00:30:00 +02:00
|
|
|
int save_bbx1 = 0, save_bby1 = 0, save_bbx2 = 0, save_bby2 = 0;
|
2022-01-28 04:44:07 +01:00
|
|
|
dbg(1, "draw_graph_all(): flags=%d\n", flags);
|
2021-12-29 05:11:39 +01:00
|
|
|
/* save bbox data, since draw_graph_all() is called from draw() which may be called after a bbox(SET) */
|
2022-09-21 11:25:45 +02:00
|
|
|
sch_loaded = (sch_waves_loaded() >= 0);
|
|
|
|
|
dbg(1, "draw_graph_all(): sch_loaded=%d\n", sch_loaded);
|
2022-09-18 05:29:16 +02:00
|
|
|
hide_graphs = tclgetboolvar("hide_empty_graphs");
|
2021-12-26 04:09:55 +01:00
|
|
|
if(sch_loaded || !hide_graphs) {
|
2022-09-21 11:25:45 +02:00
|
|
|
if(xctx->bbox_set) {
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
bbox_set = 1;
|
|
|
|
|
save_bbx1 = xctx->bbx1;
|
|
|
|
|
save_bby1 = xctx->bby1;
|
|
|
|
|
save_bbx2 = xctx->bbx2;
|
|
|
|
|
save_bby2 = xctx->bby2;
|
|
|
|
|
bbox(END, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
}
|
|
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
cairo_save(xctx->cairo_ctx);
|
|
|
|
|
cairo_save(xctx->cairo_save_ctx);
|
2022-12-25 10:42:07 +01:00
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create("Sans-Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font);
|
2022-12-25 23:13:59 +01:00
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
#endif
|
2022-01-05 23:07:08 +01:00
|
|
|
if(xctx->draw_single_layer==-1 || GRIDLAYER == xctx->draw_single_layer) {
|
2023-02-18 09:44:11 +01:00
|
|
|
if(xctx->enable_layer[GRIDLAYER]) for(i = 0; i < xctx->rects[GRIDLAYER]; ++i) {
|
2022-01-05 23:07:08 +01:00
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
2022-01-02 04:42:15 +01:00
|
|
|
if(r->flags & 1) {
|
2025-01-07 04:20:32 +01:00
|
|
|
int flags2;
|
2023-02-26 11:22:19 +01:00
|
|
|
setup_graph_data(i, 0, &xctx->graph_struct);
|
2025-01-07 04:20:32 +01:00
|
|
|
flags2 = flags | (xctx->graph_flags & (128 | 256)); /* include drawing hcursors if enabled */
|
|
|
|
|
draw_graph(i, flags2, &xctx->graph_struct, NULL); /* draw data in each graph box */
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
}
|
2021-12-22 04:39:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
cairo_restore(xctx->cairo_ctx);
|
|
|
|
|
cairo_restore(xctx->cairo_save_ctx);
|
|
|
|
|
#endif
|
|
|
|
|
/* restore previous bbox */
|
|
|
|
|
if(bbox_set) {
|
|
|
|
|
xctx->bbx1 = save_bbx1;
|
|
|
|
|
xctx->bby1 = save_bby1;
|
|
|
|
|
xctx->bbx2 = save_bbx2;
|
|
|
|
|
xctx->bby2 = save_bby2;
|
2022-09-21 11:25:45 +02:00
|
|
|
xctx->bbox_set = 1;
|
Better axis label placement, smooth waves move/zoom with mouse, compile option to store sim data as floats instead of doubles, option for axis scaling (p,n,u,m,k,M,G), load waveforms command is a toggle (load / free)
2021-12-24 12:17:00 +01:00
|
|
|
bbox(SET, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
}
|
2021-12-22 04:39:23 +01:00
|
|
|
}
|
2021-12-20 00:01:10 +01:00
|
|
|
}
|
|
|
|
|
|
2022-01-21 03:00:23 +01:00
|
|
|
#if HAS_CAIRO==1
|
2023-01-17 01:23:34 +01:00
|
|
|
cairo_status_t png_reader(void *in_closure, unsigned char *out_data, unsigned int length)
|
2022-01-19 18:39:23 +01:00
|
|
|
{
|
|
|
|
|
png_to_byte_closure_t *closure = (png_to_byte_closure_t *) in_closure;
|
2022-01-20 18:28:29 +01:00
|
|
|
if(!closure->buffer) return CAIRO_STATUS_READ_ERROR;
|
2022-01-19 18:39:23 +01:00
|
|
|
memcpy(out_data, closure->buffer + closure->pos, length);
|
|
|
|
|
closure->pos += length;
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 11:09:49 +01:00
|
|
|
cairo_status_t png_writer(void *in_closure, const unsigned char *in_data, unsigned int length)
|
2022-01-19 18:39:23 +01:00
|
|
|
{
|
|
|
|
|
png_to_byte_closure_t *closure = (png_to_byte_closure_t *) in_closure;
|
2022-01-21 02:16:51 +01:00
|
|
|
if(!in_data) return CAIRO_STATUS_WRITE_ERROR;
|
2022-01-19 18:39:23 +01:00
|
|
|
if(closure->pos + length > closure->size) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_realloc(_ALLOC_ID_, &closure->buffer, closure->pos + length + 65536);
|
2022-01-19 18:39:23 +01:00
|
|
|
closure->size = closure->pos + length + 65536;
|
|
|
|
|
}
|
|
|
|
|
memcpy(closure->buffer + closure->pos, in_data, length);
|
|
|
|
|
closure->pos += length;
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
}
|
2022-01-21 02:51:58 +01:00
|
|
|
#endif
|
2022-01-19 18:39:23 +01:00
|
|
|
|
2024-10-28 19:15:13 +01:00
|
|
|
/*
|
|
|
|
|
* The memmem() function finds the start of the first occurrence of the
|
|
|
|
|
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
|
|
|
|
|
* length 'hlen'.
|
|
|
|
|
*
|
|
|
|
|
* The return value is a pointer to the beginning of the sub-string, or
|
|
|
|
|
* NULL if the substring is not found.
|
|
|
|
|
*/
|
|
|
|
|
void *my_memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen)
|
|
|
|
|
{
|
|
|
|
|
int needle_first;
|
|
|
|
|
const char *p = haystack;
|
|
|
|
|
size_t plen = hlen;
|
|
|
|
|
|
|
|
|
|
if (!nlen) return NULL;
|
|
|
|
|
needle_first = *(unsigned char *)needle;
|
|
|
|
|
|
|
|
|
|
while (plen >= nlen && (p = memchr(p, needle_first, plen - nlen + 1)))
|
|
|
|
|
{
|
|
|
|
|
if (!memcmp(p, needle, nlen)) return (void *)p;
|
|
|
|
|
p++;
|
|
|
|
|
plen = hlen - (p - (char *)haystack);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-15 12:57:42 +01:00
|
|
|
#if HAS_CAIRO==1
|
2024-03-17 01:32:10 +01:00
|
|
|
/* what:
|
|
|
|
|
* 1: invert colors
|
|
|
|
|
* 2: set white to transparent
|
|
|
|
|
* 4: set black to transparent
|
|
|
|
|
* 8: set transparent to white
|
|
|
|
|
* 16: set transparent to black
|
2024-03-18 10:54:10 +01:00
|
|
|
* 32: blend with white, remove alpha
|
|
|
|
|
* 64: blend with black, remove alpha
|
2024-03-17 01:32:10 +01:00
|
|
|
* 256: write back into `image_data` attribute
|
|
|
|
|
*/
|
|
|
|
|
int edit_image(int what, xRect *r)
|
2024-03-15 00:37:27 +01:00
|
|
|
{
|
2024-03-16 12:50:42 +01:00
|
|
|
cairo_t *ct;
|
|
|
|
|
unsigned char *data;
|
|
|
|
|
cairo_surface_t *newsfc;
|
|
|
|
|
cairo_format_t format;
|
2024-03-17 01:32:10 +01:00
|
|
|
int jpg, size_x, size_y, stride, x, y;
|
|
|
|
|
xEmb_image *emb_ptr = r->extraptr;
|
2024-03-18 01:08:51 +01:00
|
|
|
cairo_surface_t **surface;
|
|
|
|
|
const char *attr;
|
|
|
|
|
|
|
|
|
|
if(!emb_ptr || !emb_ptr->image) return 0;
|
|
|
|
|
attr = get_tok_value(r->prop_ptr, "image_data", 0);
|
|
|
|
|
surface = &emb_ptr->image;
|
2024-03-16 12:50:42 +01:00
|
|
|
cairo_surface_flush(*surface);
|
2024-03-17 01:32:10 +01:00
|
|
|
if(attr[0]) {
|
|
|
|
|
if(!strncmp(attr, "/9j/", 4)) jpg = 1;
|
|
|
|
|
else if(!strncmp(attr, "iVBOR", 5)) jpg = 0;
|
|
|
|
|
else jpg = -1; /* some invalid data */
|
|
|
|
|
} else {
|
|
|
|
|
jpg = -1;
|
|
|
|
|
}
|
|
|
|
|
if(jpg == -1) return 0;
|
2024-03-16 12:50:42 +01:00
|
|
|
format = cairo_image_surface_get_format(*surface);
|
|
|
|
|
size_x = cairo_image_surface_get_width(*surface);
|
|
|
|
|
size_y = cairo_image_surface_get_height(*surface);
|
|
|
|
|
stride = cairo_image_surface_get_stride(*surface);
|
|
|
|
|
/* add alpha channel if missing */
|
|
|
|
|
if(format != CAIRO_FORMAT_ARGB32) {
|
|
|
|
|
newsfc = cairo_surface_create_similar_image(*surface, CAIRO_FORMAT_ARGB32, size_x, size_y);
|
|
|
|
|
ct = cairo_create(newsfc);
|
|
|
|
|
cairo_set_source_surface(ct, *surface, 0, 0);
|
|
|
|
|
cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
|
|
|
|
|
cairo_paint(ct);
|
|
|
|
|
cairo_destroy(ct);
|
|
|
|
|
cairo_surface_destroy(*surface);
|
|
|
|
|
*surface = newsfc;
|
|
|
|
|
}
|
|
|
|
|
data = cairo_image_surface_get_data(*surface);
|
2024-03-15 00:37:27 +01:00
|
|
|
for(x = 0; x < size_x; x++) {
|
|
|
|
|
for(y = 0; y < size_y; y++) {
|
2024-03-16 12:50:42 +01:00
|
|
|
unsigned char *ptr = data + y * stride + x * 4;
|
2024-03-17 01:32:10 +01:00
|
|
|
unsigned char a = ptr[3];
|
|
|
|
|
unsigned char r = ptr[2];
|
|
|
|
|
unsigned char g = ptr[1];
|
|
|
|
|
unsigned char b = ptr[0];
|
|
|
|
|
|
|
|
|
|
/* invert colors */
|
|
|
|
|
if(what & 1) {
|
|
|
|
|
r = a - r;
|
|
|
|
|
g = a - g;
|
|
|
|
|
b = a - b;
|
|
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
|
2024-03-17 01:32:10 +01:00
|
|
|
/* set white to transparent */
|
|
|
|
|
if(what & 2) {
|
|
|
|
|
if(r > 242 && g > 242 && b >242) {r = g = b = a = 0;}
|
2024-03-15 00:37:27 +01:00
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
|
2024-03-17 01:32:10 +01:00
|
|
|
/* set black to transparent */
|
|
|
|
|
if(what & 4) {
|
|
|
|
|
if(r < 13 && g < 13 && b < 13) {r = g = b = a = 0;}
|
|
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
|
2024-03-17 01:32:10 +01:00
|
|
|
/* set transparent to white */
|
|
|
|
|
if(what & 8) {
|
|
|
|
|
if(a == 0) {r = g = b = a = 0xff;}
|
|
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
|
2024-03-17 01:32:10 +01:00
|
|
|
/* set transparent to black */
|
|
|
|
|
if(what & 16) {
|
|
|
|
|
if(a == 0) {r = g = b = 0x00; a = 0xff;}
|
|
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
|
2024-03-18 10:54:10 +01:00
|
|
|
/* remove alpha, blend with white */
|
|
|
|
|
if(what & 32) {
|
|
|
|
|
r += (unsigned char)(0xff - a);
|
|
|
|
|
g += (unsigned char)(0xff - a);
|
|
|
|
|
b += (unsigned char)(0xff - a);
|
|
|
|
|
a = (unsigned char)0xff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* remove alpha, blend with black */
|
|
|
|
|
if(what & 64) {
|
|
|
|
|
a = (unsigned char)0xff;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-16 12:50:42 +01:00
|
|
|
/* write result back */
|
2024-03-17 01:32:10 +01:00
|
|
|
ptr[3] = a;
|
|
|
|
|
ptr[2] = r;
|
|
|
|
|
ptr[1] = g;
|
|
|
|
|
ptr[0] = b;
|
2024-03-15 00:37:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
cairo_surface_mark_dirty(*surface);
|
2024-03-17 01:32:10 +01:00
|
|
|
|
|
|
|
|
/* write back modified image to image_data attribute */
|
|
|
|
|
if(what & 256) {
|
|
|
|
|
char *encoded_data = NULL;
|
|
|
|
|
size_t olength;
|
|
|
|
|
png_to_byte_closure_t closure;
|
|
|
|
|
if(jpg == 0) {
|
|
|
|
|
/* write PNG to in-memory buffer */
|
|
|
|
|
closure.buffer = NULL;
|
|
|
|
|
closure.size = 0;
|
|
|
|
|
closure.pos = 0;
|
|
|
|
|
cairo_surface_write_to_png_stream(emb_ptr->image, png_writer, &closure);
|
|
|
|
|
closure.size = closure.pos;
|
|
|
|
|
} else if(jpg == 1) {
|
|
|
|
|
/* write JPG to in-memory buffer */
|
|
|
|
|
#if defined(HAS_LIBJPEG)
|
|
|
|
|
int jpeg_quality;
|
|
|
|
|
const char *ptr;
|
|
|
|
|
ptr = get_tok_value(r->prop_ptr, "jpeg_quality", 0);
|
|
|
|
|
jpeg_quality = 75;
|
|
|
|
|
if(ptr[0]) jpeg_quality = atoi(ptr);
|
|
|
|
|
closure.buffer = NULL;
|
|
|
|
|
closure.size = 0;
|
|
|
|
|
closure.pos = 0;
|
|
|
|
|
cairo_image_surface_write_to_jpeg_mem(emb_ptr->image, &closure.buffer, &closure.pos, jpeg_quality);
|
|
|
|
|
closure.size = closure.pos;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* put base64 encoded data to rect image_data attribute */
|
|
|
|
|
encoded_data = base64_encode((unsigned char *)closure.buffer, closure.size, &olength, 0);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "image_data", encoded_data));
|
|
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
|
|
|
|
my_free(_ALLOC_ID_, &encoded_data);
|
2024-03-17 01:32:10 +01:00
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
dbg(1, "size_x = %d, size_y = %d, stride = %d\n", size_x, size_y, stride);
|
2024-03-17 01:32:10 +01:00
|
|
|
return 1;
|
2024-03-15 00:37:27 +01:00
|
|
|
}
|
2024-03-17 10:11:57 +01:00
|
|
|
|
|
|
|
|
/* returns a cairo surface.
|
|
|
|
|
* `filename` should be a png or jpeg image or anything else that can be converted to png
|
|
|
|
|
* or jpeg via a `filter` pipeline. (example: filter="gm convert - png:-")
|
|
|
|
|
* buffer returns the content of filename or the filtered result if filter is given.
|
|
|
|
|
* `size` is set to the size of the returned data */
|
|
|
|
|
static cairo_surface_t *get_surface_from_file(const char *filename, const char *filter,
|
|
|
|
|
unsigned char **buffer, size_t *size)
|
|
|
|
|
{
|
2024-10-28 12:45:50 +01:00
|
|
|
int jpg = 0;
|
|
|
|
|
png_to_byte_closure_t closure = {NULL, 0L, 0L};
|
|
|
|
|
size_t filesize = 0;
|
|
|
|
|
char *filedata = NULL;
|
|
|
|
|
FILE *fd;
|
|
|
|
|
struct stat buf;
|
|
|
|
|
int svg = 0;
|
|
|
|
|
cairo_surface_t *surface = NULL;
|
|
|
|
|
|
|
|
|
|
*buffer = NULL;
|
|
|
|
|
*size = 0;
|
|
|
|
|
if(filename && filename[0]) {
|
|
|
|
|
if(stat(filename, &buf)) {
|
|
|
|
|
dbg(0, "get_surface_from_file(): file %s not found.\n", filename);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
filesize = (size_t)buf.st_size;
|
|
|
|
|
if(filesize > 0) {
|
2025-02-18 02:25:55 +01:00
|
|
|
fd = my_fopen(filename, fopen_read_mode);
|
2024-10-28 12:45:50 +01:00
|
|
|
if(fd) {
|
|
|
|
|
size_t bytes_read;
|
2025-04-18 02:02:15 +02:00
|
|
|
filedata = my_malloc(_ALLOC_ID_, filesize);
|
2024-10-28 12:45:50 +01:00
|
|
|
if((bytes_read = fread(filedata, 1, filesize, fd)) < filesize) {
|
|
|
|
|
filesize = bytes_read;
|
|
|
|
|
dbg(0, "get_surface_from_file(): less bytes read than expected from %s, got %ld bytes\n",
|
|
|
|
|
filename, bytes_read);
|
2024-03-17 10:11:57 +01:00
|
|
|
}
|
2024-10-28 12:45:50 +01:00
|
|
|
fclose(fd);
|
2024-03-17 10:11:57 +01:00
|
|
|
}
|
2024-10-28 12:45:50 +01:00
|
|
|
} else {
|
|
|
|
|
dbg(0, "get_surface_from_file(): file %s has zero size\n", filename);
|
|
|
|
|
return NULL;
|
2024-03-17 10:11:57 +01:00
|
|
|
}
|
2024-10-28 12:45:50 +01:00
|
|
|
if(filedata && my_memmem(filedata, filesize, "<svg", 4) &&
|
2024-10-27 03:04:55 +01:00
|
|
|
my_memmem(filedata, filesize, "xmlns", 5)) {
|
|
|
|
|
if(filter) svg = 1;
|
2024-10-28 12:45:50 +01:00
|
|
|
else {
|
|
|
|
|
dbg(0, "get_surface_from_file():\n");
|
|
|
|
|
dbg(0, " A SVG file is specified but no 'filter' attribute to convert to png was given\n");
|
|
|
|
|
dbg(0, " May be no 'svg_to_png' variable was specified in xschemrc\n");
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &filedata);
|
2024-10-28 12:45:50 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2024-10-27 03:04:55 +01:00
|
|
|
}
|
2024-10-28 12:45:50 +01:00
|
|
|
|
2024-03-17 10:11:57 +01:00
|
|
|
if(filter) {
|
|
|
|
|
size_t filtered_img_size = 0;
|
|
|
|
|
char *filtered_img_data = NULL;
|
|
|
|
|
filter_data(filedata, filesize, &filtered_img_data, &filtered_img_size, filter);
|
2025-04-18 02:02:15 +02:00
|
|
|
if(!svg) my_free(_ALLOC_ID_, &filedata);
|
2024-03-17 10:11:57 +01:00
|
|
|
closure.buffer = (unsigned char *)filtered_img_data;
|
|
|
|
|
closure.size = filtered_img_size;
|
|
|
|
|
closure.pos = 0;
|
|
|
|
|
} else { /* no filter attribute */
|
|
|
|
|
closure.buffer = (unsigned char *)filedata;
|
|
|
|
|
filedata = NULL;
|
|
|
|
|
closure.size = filesize;
|
|
|
|
|
closure.pos = 0;
|
|
|
|
|
}
|
2024-10-28 12:45:50 +01:00
|
|
|
|
2024-03-17 10:11:57 +01:00
|
|
|
if(closure.size > 4) {
|
|
|
|
|
if(!strncmp((char *)closure.buffer, "\x89PNG", 4)) jpg = 0;
|
|
|
|
|
else if(!strncmp((char *)closure.buffer, "\xFF\xD8\xFF", 3)) jpg = 1;
|
|
|
|
|
else jpg = -1;
|
|
|
|
|
} else {
|
|
|
|
|
jpg = -1;
|
|
|
|
|
}
|
2024-04-22 18:00:15 +02:00
|
|
|
if(closure.buffer) {
|
|
|
|
|
if(jpg == 0) {
|
|
|
|
|
surface = cairo_image_surface_create_from_png_stream(png_reader, &closure);
|
|
|
|
|
} else if(jpg == 1) {
|
|
|
|
|
#if defined(HAS_LIBJPEG)
|
|
|
|
|
surface = cairo_image_surface_create_from_jpeg_mem(closure.buffer, closure.size);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if(!surface || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
2025-02-17 17:53:49 +01:00
|
|
|
if(jpg != 1) dbg(0, "get_surface_from_file(): failure creating image surface from %s\n", filename);
|
2024-04-22 18:00:15 +02:00
|
|
|
if(surface) cairo_surface_destroy(surface);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
2024-04-22 18:00:15 +02:00
|
|
|
*buffer = NULL;
|
|
|
|
|
*size = 0;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2024-03-17 10:11:57 +01:00
|
|
|
}
|
2024-10-27 03:04:55 +01:00
|
|
|
if(svg) { /* if the file type is SVG return in buffer the plain file,
|
|
|
|
|
* not the filtered content, This way we don't lose resolution */
|
|
|
|
|
*buffer = (unsigned char *)filedata;
|
|
|
|
|
*size = filesize;
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
2024-10-27 03:04:55 +01:00
|
|
|
} else {
|
|
|
|
|
*buffer = closure.buffer;
|
|
|
|
|
*size = closure.size;
|
|
|
|
|
}
|
2024-10-28 12:45:50 +01:00
|
|
|
} /* if(filename...) */
|
|
|
|
|
else {
|
|
|
|
|
dbg(0, "get_surface_from_file(): no filename was given\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return surface;
|
2024-03-17 10:11:57 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-27 03:04:55 +01:00
|
|
|
static cairo_surface_t *get_surface_from_b64data(const char *attr, size_t attr_len, const char *filter)
|
2024-03-17 01:32:10 +01:00
|
|
|
{
|
2024-10-27 03:04:55 +01:00
|
|
|
int jpg = -1; /* 0: png, 1: jpg, 2: svg, -1: invalid data */
|
2024-03-17 01:32:10 +01:00
|
|
|
png_to_byte_closure_t closure;
|
|
|
|
|
size_t data_size;
|
|
|
|
|
cairo_surface_t *surface = NULL;
|
|
|
|
|
|
2024-03-18 01:08:51 +01:00
|
|
|
closure.buffer = base64_decode(attr, attr_len, &data_size);
|
2024-03-17 01:32:10 +01:00
|
|
|
closure.pos = 0;
|
|
|
|
|
closure.size = data_size; /* should not be necessary */
|
2024-10-27 03:04:55 +01:00
|
|
|
|
|
|
|
|
if(!strncmp(attr, "/9j/", 4)) jpg = 1; /* jpg */
|
|
|
|
|
else if(!strncmp(attr, "iVBOR", 5)) jpg = 0; /* png */
|
|
|
|
|
else if(my_memmem(closure.buffer, closure.size, "<svg", 4) &&
|
|
|
|
|
my_memmem(closure.buffer, closure.size, "xmlns", 5)) {
|
|
|
|
|
if(filter) jpg = 2; /* svg */
|
|
|
|
|
}
|
|
|
|
|
else jpg = -1; /* some invalid data */
|
|
|
|
|
|
|
|
|
|
if(jpg == -1) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
2024-10-27 03:04:55 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 01:32:10 +01:00
|
|
|
if(closure.buffer == NULL) {
|
2024-03-17 10:11:57 +01:00
|
|
|
dbg(0, "get_surface_from_b64data(): decoding base64 data for image failed\n");
|
2024-03-17 01:32:10 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2024-10-27 03:04:55 +01:00
|
|
|
|
|
|
|
|
if(jpg == 0) { /* png */
|
2024-03-17 01:32:10 +01:00
|
|
|
surface = cairo_image_surface_create_from_png_stream(png_reader, &closure);
|
2024-10-27 03:04:55 +01:00
|
|
|
} else if(jpg == 1) { /* jpg */
|
2024-03-17 01:32:10 +01:00
|
|
|
#if defined(HAS_LIBJPEG)
|
|
|
|
|
surface = cairo_image_surface_create_from_jpeg_mem(closure.buffer, closure.size);
|
|
|
|
|
#endif
|
2024-10-27 03:04:55 +01:00
|
|
|
} else if(jpg == 2) { /* svg */
|
|
|
|
|
size_t filtered_img_size = 0;
|
|
|
|
|
char *filtered_img_data = NULL;
|
2024-10-28 12:12:11 +01:00
|
|
|
int ret =
|
|
|
|
|
filter_data((char *)closure.buffer, closure.size, &filtered_img_data, &filtered_img_size, filter);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
2024-10-27 03:04:55 +01:00
|
|
|
closure.buffer = (unsigned char *)filtered_img_data;
|
|
|
|
|
closure.size = filtered_img_size;
|
|
|
|
|
closure.pos = 0;
|
2024-10-28 12:12:11 +01:00
|
|
|
if(!ret) {
|
|
|
|
|
surface = cairo_image_surface_create_from_png_stream(png_reader, &closure);
|
|
|
|
|
} else {
|
|
|
|
|
surface = NULL;
|
2025-04-18 02:02:15 +02:00
|
|
|
if(closure.buffer) my_free(_ALLOC_ID_, &closure.buffer);
|
2024-10-28 12:12:11 +01:00
|
|
|
return NULL;;
|
|
|
|
|
}
|
2024-03-17 01:32:10 +01:00
|
|
|
}
|
|
|
|
|
if(!surface || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
2024-10-28 12:12:11 +01:00
|
|
|
dbg(0, "get_surface_from_b64data(): failure creating image surface from \"image_data\" attribute\n");
|
2024-03-17 01:32:10 +01:00
|
|
|
if(surface) cairo_surface_destroy(surface);
|
|
|
|
|
surface = NULL;
|
|
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
2024-03-17 01:32:10 +01:00
|
|
|
return surface;
|
|
|
|
|
}
|
2024-03-17 20:16:48 +01:00
|
|
|
#endif /* HAS_CAIRO==1 */
|
2024-03-17 01:32:10 +01:00
|
|
|
|
2022-01-24 22:58:30 +01:00
|
|
|
/* rot and flip for rotated / flipped symbols
|
2022-09-15 19:39:16 +02:00
|
|
|
* dr: 1 draw image
|
2024-03-16 12:50:42 +01:00
|
|
|
* 0 only load image and build base64
|
2022-01-24 22:58:30 +01:00
|
|
|
*/
|
2024-03-16 12:50:42 +01:00
|
|
|
int draw_image(int dr, xRect *r, double *x1, double *y1, double *x2, double *y2, int rot, int flip)
|
2022-01-19 12:15:33 +01:00
|
|
|
{
|
2024-03-15 13:59:52 +01:00
|
|
|
#if HAS_CAIRO==1
|
2022-01-24 22:58:30 +01:00
|
|
|
const char *ptr;
|
|
|
|
|
int w,h;
|
2022-01-23 22:41:24 +01:00
|
|
|
double x, y, rw, rh;
|
2022-01-22 04:55:59 +01:00
|
|
|
double sx1, sy1, sx2, sy2, alpha;
|
2022-01-23 04:51:41 +01:00
|
|
|
char filename[PATH_MAX];
|
2022-01-24 22:58:30 +01:00
|
|
|
const char *attr ;
|
|
|
|
|
double xx1, yy1, scalex, scaley;
|
|
|
|
|
xEmb_image *emb_ptr;
|
2024-03-18 01:08:51 +01:00
|
|
|
size_t attr_len;
|
2024-10-27 03:04:55 +01:00
|
|
|
char *filter = NULL;
|
2022-01-20 00:47:15 +01:00
|
|
|
|
2024-03-16 12:50:42 +01:00
|
|
|
if(xctx->only_probes) return 0;
|
2024-09-23 15:57:32 +02:00
|
|
|
xx1 = *x1; yy1 = *y1; /* image anchor point */
|
2024-10-26 18:09:12 +02:00
|
|
|
RECTORDER(*x1, *y1, *x2, *y2);
|
2022-01-24 22:58:30 +01:00
|
|
|
|
|
|
|
|
/* screen position */
|
|
|
|
|
sx1=X_TO_SCREEN(*x1);
|
|
|
|
|
sy1=Y_TO_SCREEN(*y1);
|
|
|
|
|
sx2=X_TO_SCREEN(*x2);
|
|
|
|
|
sy2=Y_TO_SCREEN(*y2);
|
|
|
|
|
if(RECT_OUTSIDE(sx1, sy1, sx2, sy2,
|
2024-03-16 12:50:42 +01:00
|
|
|
xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) return 0;
|
2022-01-24 22:58:30 +01:00
|
|
|
set_rect_extraptr(1, r); /* create r->extraptr pointing to a xEmb_image struct */
|
|
|
|
|
emb_ptr = r->extraptr;
|
|
|
|
|
my_strncpy(filename, get_tok_value(r->prop_ptr, "image", 0), S(filename));
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup(_ALLOC_ID_, &filter, get_tok_value(r->prop_ptr, "filter", 0));
|
2024-03-16 12:50:42 +01:00
|
|
|
/******* read image from in-memory buffer ... *******/
|
2022-01-24 22:58:30 +01:00
|
|
|
if(emb_ptr && emb_ptr->image) {
|
2024-03-16 12:50:42 +01:00
|
|
|
/* nothing to do, image is already created */
|
|
|
|
|
/******* ... or read PNG from image_data attribute *******/
|
2024-03-18 01:08:51 +01:00
|
|
|
} else if( (attr = get_tok_value(r->prop_ptr, "image_data", 0))[0] && (attr_len = strlen(attr)) > 5) {
|
2024-10-27 03:04:55 +01:00
|
|
|
emb_ptr->image = get_surface_from_b64data(attr, attr_len, filter);
|
2024-03-17 01:32:10 +01:00
|
|
|
if(!emb_ptr->image) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &filter);
|
2024-03-16 12:50:42 +01:00
|
|
|
return 0;
|
2024-03-14 11:09:49 +01:00
|
|
|
}
|
2024-03-16 12:50:42 +01:00
|
|
|
/******* ... or read PNG from file (image attribute) *******/
|
2024-10-27 03:04:55 +01:00
|
|
|
} else if(filename[0]) {
|
2024-03-17 10:11:57 +01:00
|
|
|
unsigned char *buffer = NULL;
|
|
|
|
|
size_t size = 0;
|
2024-03-15 00:37:27 +01:00
|
|
|
char *encoded_data = NULL;
|
2022-01-24 22:58:30 +01:00
|
|
|
size_t olength;
|
2024-03-16 12:50:42 +01:00
|
|
|
|
2024-10-27 03:04:55 +01:00
|
|
|
/* if filename is a SVG file buffer will be the plain svg file content, not the filtered data */
|
2024-03-17 10:11:57 +01:00
|
|
|
emb_ptr->image = get_surface_from_file(filename, filter, &buffer, &size);
|
|
|
|
|
if(!emb_ptr->image) {
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &filter);
|
2024-03-16 12:50:42 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
2024-03-15 00:37:27 +01:00
|
|
|
/* put base64 encoded data to rect image_data attribute */
|
2024-03-17 10:11:57 +01:00
|
|
|
encoded_data = base64_encode((unsigned char *)buffer, size, &olength, 0);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_strdup2(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "image_data", encoded_data));
|
|
|
|
|
my_free(_ALLOC_ID_, &encoded_data);
|
|
|
|
|
my_free(_ALLOC_ID_, &buffer);
|
2024-03-17 01:32:10 +01:00
|
|
|
} else { /* no emb_ptr->image and no "image_data" attribute */
|
2024-03-16 12:50:42 +01:00
|
|
|
return 0;
|
2022-02-05 00:28:06 +01:00
|
|
|
}
|
2022-01-24 22:58:30 +01:00
|
|
|
ptr = get_tok_value(r->prop_ptr, "alpha", 0);
|
|
|
|
|
alpha = 1.0;
|
|
|
|
|
if(ptr[0]) alpha = atof(ptr);
|
|
|
|
|
w = cairo_image_surface_get_width (emb_ptr->image);
|
|
|
|
|
h = cairo_image_surface_get_height (emb_ptr->image);
|
|
|
|
|
dbg(1, "draw_image() w=%d, h=%d\n", w, h);
|
|
|
|
|
x = X_TO_SCREEN(xx1);
|
|
|
|
|
y = Y_TO_SCREEN(yy1);
|
2024-09-23 15:57:32 +02:00
|
|
|
dbg(1, "draw_image() x=%g, y=%g\n", x, y);
|
2022-01-24 22:58:30 +01:00
|
|
|
if(r->flags & 2048) { /* resize container rectangle to fit image */
|
|
|
|
|
*x2 = *x1 + w;
|
|
|
|
|
*y2 = *y1 + h;
|
|
|
|
|
scalex = xctx->mooz;
|
|
|
|
|
scaley = xctx->mooz;
|
|
|
|
|
} else { /* resize image to fit in rectangle */
|
2022-04-27 13:18:45 +02:00
|
|
|
rw = abs((int)(*x2 - *x1));
|
|
|
|
|
rh = abs((int)(*y2 - *y1));
|
2023-01-16 19:33:59 +01:00
|
|
|
if (rot == 1 || rot == 3)
|
|
|
|
|
{
|
|
|
|
|
scalex = rh/w * xctx->mooz;
|
|
|
|
|
scaley = rw/h * xctx->mooz;
|
|
|
|
|
}else
|
|
|
|
|
{
|
|
|
|
|
scalex = rw/w * xctx->mooz;
|
|
|
|
|
scaley = rh/h * xctx->mooz;
|
|
|
|
|
}
|
2022-01-24 22:58:30 +01:00
|
|
|
}
|
2024-09-23 15:57:32 +02:00
|
|
|
dbg(1, "draw_image() : rectangle coords: %g %g %g %g\n", *x1, *y1, *x2, *y2);
|
2024-03-17 01:56:59 +01:00
|
|
|
if(dr) {
|
|
|
|
|
cairo_save(xctx->cairo_ctx);
|
|
|
|
|
cairo_save(xctx->cairo_save_ctx);
|
|
|
|
|
}
|
2022-09-15 19:39:16 +02:00
|
|
|
if(dr && xctx->draw_pixmap) {
|
2022-01-24 22:58:30 +01:00
|
|
|
cairo_translate(xctx->cairo_save_ctx, x, y);
|
2023-01-16 19:33:59 +01:00
|
|
|
cairo_rotate(xctx->cairo_save_ctx, rot * XSCH_PI * 0.5);
|
2022-01-24 22:58:30 +01:00
|
|
|
if(flip && (rot == 0 || rot == 2)) cairo_scale(xctx->cairo_save_ctx, -scalex, scaley);
|
2023-01-16 19:33:59 +01:00
|
|
|
else if(flip && (rot == 1 || rot == 3)) cairo_scale(xctx->cairo_save_ctx, -scalex, scaley);
|
2022-01-24 22:58:30 +01:00
|
|
|
else cairo_scale(xctx->cairo_save_ctx, scalex, scaley);
|
2024-09-23 15:57:32 +02:00
|
|
|
|
2022-01-24 22:58:30 +01:00
|
|
|
cairo_set_source_surface(xctx->cairo_save_ctx, emb_ptr->image, 0. , 0.);
|
|
|
|
|
cairo_rectangle(xctx->cairo_save_ctx, 0, 0, w , h );
|
|
|
|
|
/* cairo_fill(xctx->cairo_save_ctx);
|
|
|
|
|
* cairo_stroke(xctx->cairo_save_ctx); */
|
|
|
|
|
cairo_clip(xctx->cairo_save_ctx);
|
|
|
|
|
cairo_paint_with_alpha(xctx->cairo_save_ctx, alpha);
|
2024-09-23 23:17:19 +02:00
|
|
|
cairo_surface_flush(xctx->cairo_save_sfc);
|
2022-01-24 22:58:30 +01:00
|
|
|
}
|
2022-09-15 19:39:16 +02:00
|
|
|
if(dr && xctx->draw_window) {
|
2022-01-24 22:58:30 +01:00
|
|
|
cairo_translate(xctx->cairo_ctx, x, y);
|
2023-01-16 19:33:59 +01:00
|
|
|
cairo_rotate(xctx->cairo_ctx, rot * XSCH_PI * 0.5);
|
2022-01-24 22:58:30 +01:00
|
|
|
if(flip && (rot == 0 || rot == 2)) cairo_scale(xctx->cairo_ctx, -scalex, scaley);
|
2025-02-17 17:53:49 +01:00
|
|
|
else if(flip && (rot == 1 || rot == 3)) cairo_scale(xctx->cairo_ctx, -scalex, scaley);
|
2022-01-24 22:58:30 +01:00
|
|
|
else cairo_scale(xctx->cairo_ctx, scalex, scaley);
|
|
|
|
|
cairo_set_source_surface(xctx->cairo_ctx, emb_ptr->image, 0. , 0.);
|
|
|
|
|
cairo_rectangle(xctx->cairo_ctx, 0, 0, w , h );
|
|
|
|
|
/* cairo_fill(xctx->cairo_ctx);
|
|
|
|
|
* cairo_stroke(xctx->cairo_ctx); */
|
|
|
|
|
cairo_clip(xctx->cairo_ctx);
|
|
|
|
|
cairo_paint_with_alpha(xctx->cairo_ctx, alpha);
|
2024-09-23 23:17:19 +02:00
|
|
|
cairo_surface_flush(xctx->cairo_sfc);
|
2022-01-24 22:58:30 +01:00
|
|
|
}
|
2022-09-15 19:39:16 +02:00
|
|
|
if(dr) {
|
2022-01-24 22:58:30 +01:00
|
|
|
cairo_restore(xctx->cairo_ctx);
|
|
|
|
|
cairo_restore(xctx->cairo_save_ctx);
|
|
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &filter);
|
2022-01-24 22:58:30 +01:00
|
|
|
#endif
|
2024-03-16 12:50:42 +01:00
|
|
|
return 1;
|
2022-01-24 22:58:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void draw_images_all(void)
|
|
|
|
|
{
|
|
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
int i;
|
2022-01-19 12:15:33 +01:00
|
|
|
if(xctx->draw_single_layer==-1 || GRIDLAYER == xctx->draw_single_layer) {
|
2023-02-18 09:44:11 +01:00
|
|
|
if(xctx->enable_layer[GRIDLAYER]) for(i = 0; i < xctx->rects[GRIDLAYER]; ++i) {
|
2022-01-19 12:15:33 +01:00
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
|
|
|
|
if(r->flags & 1024) {
|
2022-01-24 22:58:30 +01:00
|
|
|
draw_image(1, r, &r->x1, &r->y1, &r->x2, &r->y2, 0, 0);
|
2022-01-19 12:15:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2022-01-19 18:39:23 +01:00
|
|
|
|
2025-02-21 11:12:27 +01:00
|
|
|
void svg_embedded_graph(FILE *fd, int i, double rx1, double ry1, double rx2, double ry2)
|
2022-09-15 19:39:16 +02:00
|
|
|
{
|
2025-02-21 11:12:27 +01:00
|
|
|
#ifndef __unix__
|
|
|
|
|
xRect *r = &xctx->rect[GRIDLAYER][i];
|
|
|
|
|
#endif
|
2023-04-09 15:36:06 +02:00
|
|
|
#if HAS_CAIRO==1
|
2023-12-11 10:43:09 +01:00
|
|
|
Zoom_info zi;
|
2022-09-15 19:39:16 +02:00
|
|
|
char *ptr = NULL;
|
2022-09-16 12:16:26 +02:00
|
|
|
double x1, y1, x2, y2, w, h, rw, rh, scale;
|
2022-09-15 19:39:16 +02:00
|
|
|
char transform[150];
|
|
|
|
|
png_to_byte_closure_t closure;
|
|
|
|
|
cairo_surface_t *png_sfc;
|
2023-04-27 08:49:52 +02:00
|
|
|
int save, save_draw_window, save_draw_grid, rwi, rhi;
|
2022-09-15 19:39:16 +02:00
|
|
|
size_t olength;
|
2025-02-10 01:21:22 +01:00
|
|
|
const double max_size = 2500.0;
|
2022-09-16 12:16:26 +02:00
|
|
|
|
2022-12-13 13:51:33 +01:00
|
|
|
if(!has_x) return;
|
2024-09-24 11:10:30 +02:00
|
|
|
|
|
|
|
|
/* screen position */
|
|
|
|
|
x1=X_TO_SCREEN(rx1);
|
|
|
|
|
y1=Y_TO_SCREEN(ry1);
|
|
|
|
|
x2=X_TO_SCREEN(rx2);
|
|
|
|
|
y2=Y_TO_SCREEN(ry2);
|
|
|
|
|
if(RECT_OUTSIDE(x1, y1, x2, y2,
|
|
|
|
|
xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) return;
|
|
|
|
|
|
2022-09-16 12:16:26 +02:00
|
|
|
rw = fabs(rx2 -rx1);
|
|
|
|
|
rh = fabs(ry2 - ry1);
|
2023-12-09 23:39:33 +01:00
|
|
|
scale = 3.0;
|
2025-02-10 01:21:22 +01:00
|
|
|
if(rw > rh && rw * scale > max_size) {
|
2022-09-16 12:16:26 +02:00
|
|
|
scale = max_size / rw;
|
2025-02-10 01:21:22 +01:00
|
|
|
} else if(rh * scale > max_size) {
|
2022-09-16 12:16:26 +02:00
|
|
|
scale = max_size / rh;
|
|
|
|
|
}
|
|
|
|
|
rwi = (int) (rw * scale + 1.0);
|
|
|
|
|
rhi = (int) (rh * scale + 1.0);
|
2023-12-11 10:43:09 +01:00
|
|
|
save_restore_zoom(1, &zi);
|
2023-01-20 13:25:23 +01:00
|
|
|
set_viewport_size(rwi, rhi, xctx->lw);
|
2023-12-11 10:43:09 +01:00
|
|
|
|
|
|
|
|
/* zoom_box(rx1 - xctx->lw, ry1 - xctx->lw, rx2 + xctx->lw, ry2 + xctx->lw, 1.0); */
|
|
|
|
|
|
|
|
|
|
xctx->xorigin = -rx1;
|
|
|
|
|
xctx->yorigin = -ry1;
|
|
|
|
|
xctx->zoom=(rx2-rx1)/(rwi - 1);
|
|
|
|
|
xctx->mooz = 1 / xctx->zoom;
|
|
|
|
|
|
2022-09-16 12:16:26 +02:00
|
|
|
resetwin(1, 1, 1, rwi, rhi);
|
2022-09-15 19:39:16 +02:00
|
|
|
save_draw_grid = tclgetboolvar("draw_grid");
|
|
|
|
|
tclsetvar("draw_grid", "0");
|
|
|
|
|
save_draw_window = xctx->draw_window;
|
|
|
|
|
xctx->draw_window=0;
|
|
|
|
|
xctx->draw_pixmap=1;
|
2023-04-27 08:49:52 +02:00
|
|
|
save = xctx->do_copy_area;
|
2022-09-15 19:39:16 +02:00
|
|
|
xctx->do_copy_area=0;
|
2025-02-21 11:12:27 +01:00
|
|
|
setup_graph_data(i, 0, &xctx->graph_struct);
|
|
|
|
|
draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, NULL);
|
|
|
|
|
|
2022-09-25 21:11:52 +02:00
|
|
|
#ifdef __unix__
|
2022-09-15 19:39:16 +02:00
|
|
|
png_sfc = cairo_xlib_surface_create(display, xctx->save_pixmap, visual,
|
|
|
|
|
xctx->xrect[0].width, xctx->xrect[0].height);
|
2022-09-25 21:11:52 +02:00
|
|
|
#else
|
|
|
|
|
/* pixmap doesn't work on windows
|
|
|
|
|
Copy from cairo_save_sfc and use cairo
|
|
|
|
|
to draw in the data points to embed the graph */
|
|
|
|
|
png_sfc = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, xctx->xrect[0].width, xctx->xrect[0].height);
|
|
|
|
|
cairo_t *ct = cairo_create(png_sfc);
|
|
|
|
|
cairo_set_source_surface(ct, xctx->cairo_save_sfc, 0, 0);
|
|
|
|
|
cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
|
|
|
|
|
cairo_paint(ct);
|
2025-02-21 11:12:27 +01:00
|
|
|
if(r->flags & 1) {
|
|
|
|
|
setup_graph_data(i, 0, &xctx->graph_struct);
|
|
|
|
|
draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, (void *)ct);
|
2022-09-25 21:11:52 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
2022-09-15 19:39:16 +02:00
|
|
|
closure.buffer = NULL;
|
|
|
|
|
closure.size = 0;
|
|
|
|
|
closure.pos = 0;
|
|
|
|
|
cairo_surface_write_to_png_stream(png_sfc, png_writer, &closure);
|
|
|
|
|
ptr = base64_encode(closure.buffer, closure.pos, &olength, 1);
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &closure.buffer);
|
2022-09-15 19:39:16 +02:00
|
|
|
cairo_surface_destroy(png_sfc);
|
|
|
|
|
xctx->draw_pixmap=1;
|
|
|
|
|
xctx->draw_window=save_draw_window;
|
2023-04-27 08:49:52 +02:00
|
|
|
xctx->do_copy_area=save;
|
2022-09-15 19:39:16 +02:00
|
|
|
tclsetboolvar("draw_grid", save_draw_grid);
|
2023-12-11 10:43:09 +01:00
|
|
|
save_restore_zoom(0, &zi);
|
2025-02-10 17:44:05 +01:00
|
|
|
resetwin(1, 1, 1, 0, 0);
|
2022-09-15 19:39:16 +02:00
|
|
|
|
|
|
|
|
h = fabs(y2 - y1);
|
|
|
|
|
w = fabs(x2 - x1);
|
|
|
|
|
|
|
|
|
|
my_snprintf(transform, S(transform), "transform=\"translate(%g,%g)\"", x1, y1);
|
|
|
|
|
if(ptr[0]) {
|
|
|
|
|
fprintf(fd, "<image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" %s "
|
|
|
|
|
"xlink:href=\"data:image/png;base64,%s\"/>\n",
|
|
|
|
|
0.0, 0.0, w, h, transform, ptr);
|
|
|
|
|
}
|
2025-04-18 02:02:15 +02:00
|
|
|
my_free(_ALLOC_ID_, &ptr);
|
2022-09-15 20:16:35 +02:00
|
|
|
#endif
|
2022-09-15 19:39:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-08-08 15:47:34 +02:00
|
|
|
void draw(void)
|
|
|
|
|
{
|
2022-11-07 13:34:48 +01:00
|
|
|
/* inst_ptr and wire hash iterator 20171224 */
|
|
|
|
|
double x1, y1, x2, y2;
|
|
|
|
|
Instentry *instanceptr;
|
|
|
|
|
Wireentry *wireptr;
|
|
|
|
|
int use_hash;
|
2023-05-13 13:13:35 +02:00
|
|
|
int cc, c, i = 0 /*, floaters = 0 */;
|
2022-11-07 13:34:48 +01:00
|
|
|
xSymbol *symptr;
|
|
|
|
|
int textlayer;
|
2024-03-01 00:12:44 +01:00
|
|
|
double cs;
|
2022-11-07 13:34:48 +01:00
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
const char *textfont;
|
2023-02-10 03:36:29 +01:00
|
|
|
#endif
|
|
|
|
|
|
2023-09-23 01:46:39 +02:00
|
|
|
dbg(1, "draw()\n");
|
2023-02-10 03:36:29 +01:00
|
|
|
if(!xctx || xctx->no_draw) return;
|
2025-09-15 01:38:10 +02:00
|
|
|
tk_scaling = atof(tcleval("tk scaling"));
|
2024-03-01 00:12:44 +01:00
|
|
|
cs = tclgetdoublevar("cadsnap");
|
2024-03-24 19:34:30 +01:00
|
|
|
cairo_font_scale = tclgetdoublevar("cairo_font_scale");
|
2024-03-05 02:54:35 +01:00
|
|
|
xctx->cadhalfdotsize = CADHALFDOTSIZE * (cs < 20. ? cs : 20.) / 10.;
|
2023-09-29 00:30:43 +02:00
|
|
|
xctx->crosshair_layer = tclgetintvar("crosshair_layer");
|
|
|
|
|
if(xctx->crosshair_layer < 0 ) xctx->crosshair_layer = 2;
|
|
|
|
|
if(xctx->crosshair_layer >= cadlayers ) xctx->crosshair_layer = 2;
|
2023-02-10 03:36:29 +01:00
|
|
|
#if HAS_CAIRO==1
|
2022-11-07 13:34:48 +01:00
|
|
|
#ifndef __unix__
|
|
|
|
|
clear_cairo_surface(xctx->cairo_save_ctx,
|
|
|
|
|
xctx->xrect[0].x, xctx->xrect[0].y, xctx->xrect[0].width, xctx->xrect[0].height);
|
|
|
|
|
clear_cairo_surface(xctx->cairo_ctx,
|
|
|
|
|
xctx->xrect[0].x, xctx->xrect[0].y, xctx->xrect[0].width, xctx->xrect[0].height);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
xctx->show_hidden_texts = tclgetboolvar("show_hidden_texts");
|
|
|
|
|
rebuild_selected_array();
|
|
|
|
|
if(has_x) {
|
|
|
|
|
Iterator_ctx ctx;
|
2022-11-07 14:03:27 +01:00
|
|
|
|
|
|
|
|
if(xctx->only_probes) {
|
|
|
|
|
if(tclgetboolvar("dark_colorscheme")) build_colors(-1.5, 0);
|
|
|
|
|
else build_colors(1.5, 0);
|
|
|
|
|
}
|
2023-07-07 08:30:13 +02:00
|
|
|
if(xctx->draw_pixmap) {
|
2021-11-16 22:28:10 +01:00
|
|
|
XFillRectangle(display, xctx->save_pixmap, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1,
|
2020-12-23 02:14:27 +01:00
|
|
|
xctx->areaw, xctx->areah);
|
2023-07-07 08:30:13 +02:00
|
|
|
}
|
2021-11-16 22:28:10 +01:00
|
|
|
if(xctx->draw_window)
|
|
|
|
|
XFillRectangle(display, xctx->window, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1,
|
2020-12-23 02:14:27 +01:00
|
|
|
xctx->areaw, xctx->areah);
|
2021-11-17 13:32:32 +01:00
|
|
|
dbg(1, "draw(): window: %d %d %d %d\n",xctx->areax1, xctx->areay1, xctx->areax2, xctx->areay2);
|
2022-11-07 13:34:48 +01:00
|
|
|
if(!xctx->only_probes) drawgrid();
|
2024-03-31 13:27:12 +02:00
|
|
|
/* 2: draw cursor 1
|
2024-04-01 00:25:50 +02:00
|
|
|
* 4: draw cursor 2 */
|
|
|
|
|
draw_graph_all((xctx->graph_flags & (2 | 4)) + 8); /* xctx->graph_flags for cursors */
|
2022-01-19 18:39:23 +01:00
|
|
|
draw_images_all();
|
2023-05-13 13:13:35 +02:00
|
|
|
|
2020-12-02 15:10:47 +01:00
|
|
|
x1 = X_TO_XSCHEM(xctx->areax1);
|
|
|
|
|
y1 = Y_TO_XSCHEM(xctx->areay1);
|
|
|
|
|
x2 = X_TO_XSCHEM(xctx->areax2);
|
|
|
|
|
y2 = Y_TO_XSCHEM(xctx->areay2);
|
2020-10-15 17:39:21 +02:00
|
|
|
use_hash = (xctx->wires> 2000 || xctx->instances > 2000 ) && (x2 - x1 < ITERATOR_THRESHOLD);
|
2020-08-08 15:47:34 +02:00
|
|
|
if(use_hash) {
|
|
|
|
|
hash_instances();
|
|
|
|
|
hash_wires();
|
|
|
|
|
}
|
2022-11-07 13:34:48 +01:00
|
|
|
dbg(3, "draw(): check4\n");
|
2023-02-18 09:44:11 +01:00
|
|
|
for(c=0;c<cadlayers; ++c) {
|
2025-04-06 12:58:13 +02:00
|
|
|
int draw_layer = (xctx->draw_single_layer == -1 || c == xctx->draw_single_layer);
|
2022-11-07 13:34:48 +01:00
|
|
|
cc = c; if(xctx->only_probes) cc = GRIDLAYER;
|
2025-04-06 12:58:13 +02:00
|
|
|
if(draw_layer && xctx->enable_layer[c]) for(i=0;i<xctx->lines[c]; ++i) {
|
2022-11-07 13:34:48 +01:00
|
|
|
xLine *l = &xctx->line[c][i];
|
2025-11-29 00:47:46 +01:00
|
|
|
if(l->bus == -1.0) drawline(cc, THICK, l->x1, l->y1, l->x2, l->y2, l->bus, l->dash, NULL);
|
|
|
|
|
else drawline(cc, ADD, l->x1, l->y1, l->x2, l->y2, l->bus, l->dash, NULL);
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
2025-04-06 12:58:13 +02:00
|
|
|
if(draw_layer && xctx->enable_layer[c]) for(i=0;i<xctx->rects[c]; ++i) {
|
2022-11-07 13:34:48 +01:00
|
|
|
xRect *r = &xctx->rect[c][i];
|
|
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
if(c != GRIDLAYER || !(r->flags & (1 + 1024)))
|
|
|
|
|
#else
|
|
|
|
|
if(c != GRIDLAYER || !(r->flags & 1) )
|
|
|
|
|
#endif
|
|
|
|
|
{
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(cc, ADD, r->x1, r->y1, r->x2, r->y2, r->bus, r->dash, r->ellipse_a, r->ellipse_b);
|
2024-04-05 01:34:54 +02:00
|
|
|
if(r->fill) filledrect(cc, ADD, r->x1, r->y1, r->x2, r->y2, r->fill, r->ellipse_a, r->ellipse_b);
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
2025-04-06 12:58:13 +02:00
|
|
|
if(draw_layer && xctx->enable_layer[c]) for(i=0;i<xctx->arcs[c]; ++i) {
|
2024-03-01 16:03:24 +01:00
|
|
|
xArc **arc = xctx->arc;
|
|
|
|
|
drawarc(cc, ADD, arc[c][i].x, arc[c][i].y, arc[c][i].r, arc[c][i].a, arc[c][i].b,
|
2025-11-28 13:31:45 +01:00
|
|
|
arc[c][i].fill, arc[c][i].bus, arc[c][i].dash);
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
2025-04-06 12:58:13 +02:00
|
|
|
if(draw_layer && xctx->enable_layer[c]) for(i=0;i<xctx->polygons[c]; ++i) {
|
2024-02-27 10:24:21 +01:00
|
|
|
int bezier;
|
2022-11-07 13:34:48 +01:00
|
|
|
xPoly *p = &xctx->poly[c][i];
|
2024-03-02 02:29:03 +01:00
|
|
|
bezier = 2 + !strboolcmp(get_tok_value(p->prop_ptr, "bezier", 0), "true");
|
2025-11-27 22:03:10 +01:00
|
|
|
drawpolygon(cc, NOW, p->x, p->y, p->points, p->fill, p->dash, p->bus, bezier);
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
|
|
|
|
if(use_hash) init_inst_iterator(&ctx, x1, y1, x2, y2);
|
|
|
|
|
else i = -1;
|
|
|
|
|
while(1) {
|
|
|
|
|
if(use_hash) {
|
|
|
|
|
if( !(instanceptr = inst_iterator_next(&ctx))) break;
|
|
|
|
|
i = instanceptr->n;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2022-11-07 13:34:48 +01:00
|
|
|
else {
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2022-11-07 13:34:48 +01:00
|
|
|
if(i >= xctx->instances) break;
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2022-11-07 13:34:48 +01:00
|
|
|
if(xctx->inst[i].ptr == -1 || (c > 0 && (xctx->inst[i].flags & 1)) ) continue;
|
|
|
|
|
symptr = (xctx->inst[i].ptr+ xctx->sym);
|
|
|
|
|
if(
|
|
|
|
|
c==0 || /*draw_symbol call is needed on layer 0 to avoid redundant work (outside check) */
|
|
|
|
|
symptr->lines[c] ||
|
|
|
|
|
symptr->arcs[c] ||
|
|
|
|
|
symptr->rects[c] ||
|
|
|
|
|
symptr->polygons[c] ||
|
2025-04-05 11:27:45 +02:00
|
|
|
((c==cadlayers - 1) && symptr->texts) )
|
2022-11-07 13:34:48 +01:00
|
|
|
{
|
2025-04-06 12:58:13 +02:00
|
|
|
if(c == 0 || c == cadlayers - 1 || draw_layer) {
|
2025-04-21 11:35:18 +02:00
|
|
|
draw_symbol(ADD, cc, i, c, 0, 0, 0.0, 0.0); /* ... then draw current layer */
|
|
|
|
|
if(c == cadlayers - 1) {
|
|
|
|
|
if(cc == c) draw_symbol(ADD, c + 1, i, c + 1, 0, 0, 0.0, 0.0); /* ... draw texts */
|
|
|
|
|
else draw_symbol(ADD, cc , i, c + 1, 0, 0, 0.0, 0.0); /* ... draw texts */
|
|
|
|
|
}
|
2025-04-06 12:58:13 +02:00
|
|
|
}
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-02-18 02:25:55 +01:00
|
|
|
filledrect(cc, END, 0.0, 0.0, 0.0, 0.0, 2, -1, -1); /* fill parameter must be 2! */
|
2025-11-28 13:31:45 +01:00
|
|
|
drawarc(cc, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0);
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(cc, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, -1, -1);
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(cc, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, NULL);
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
|
|
|
|
cc = WIRELAYER; if(xctx->only_probes) cc = GRIDLAYER;
|
|
|
|
|
if(xctx->draw_single_layer==-1 || xctx->draw_single_layer==WIRELAYER) {
|
|
|
|
|
if(use_hash) init_wire_iterator(&ctx, x1, y1, x2, y2);
|
|
|
|
|
else i = -1;
|
|
|
|
|
while(1) {
|
|
|
|
|
if(use_hash) {
|
|
|
|
|
if( !(wireptr = wire_iterator_next(&ctx))) break;
|
|
|
|
|
i = wireptr->n;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-02-18 09:44:11 +01:00
|
|
|
++i;
|
2022-11-07 13:34:48 +01:00
|
|
|
if(i >= xctx->wires) break;
|
|
|
|
|
}
|
2025-11-26 14:07:54 +01:00
|
|
|
if(xctx->wire[i].bus == -1.0) {
|
2022-11-07 13:34:48 +01:00
|
|
|
drawline(cc, THICK, xctx->wire[i].x1,xctx->wire[i].y1,
|
2025-11-29 00:47:46 +01:00
|
|
|
xctx->wire[i].x2,xctx->wire[i].y2, xctx->wire[i].bus, 0, NULL);
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
drawline(cc, ADD, xctx->wire[i].x1,xctx->wire[i].y1,
|
2025-11-29 00:47:46 +01:00
|
|
|
xctx->wire[i].x2,xctx->wire[i].y2, xctx->wire[i].bus, 0, NULL);
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
2022-11-07 14:23:57 +01:00
|
|
|
update_conn_cues(cc, 1, xctx->draw_window);
|
2025-02-18 02:25:55 +01:00
|
|
|
filledrect(cc, END, 0.0, 0.0, 0.0, 0.0, 2, -1, -1); /* fill parameter must be 2! */
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(cc, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, NULL);
|
2022-11-07 13:34:48 +01:00
|
|
|
}
|
2025-04-06 12:58:13 +02:00
|
|
|
for(i=0;i<xctx->texts; ++i)
|
|
|
|
|
{
|
|
|
|
|
const char *txt_ptr;
|
|
|
|
|
textlayer = xctx->text[i].layer;
|
|
|
|
|
if(!xctx->show_hidden_texts && (xctx->text[i].flags & HIDE_TEXT)) continue;
|
|
|
|
|
if(xctx->only_probes) textlayer = GRIDLAYER;
|
|
|
|
|
else if(textlayer < 0 || textlayer >= cadlayers) textlayer = TEXTLAYER;
|
|
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
if(!xctx->enable_layer[textlayer]) continue;
|
|
|
|
|
if(xctx->draw_single_layer != -1 && xctx->draw_single_layer != textlayer) continue;
|
|
|
|
|
textfont = xctx->text[i].font;
|
|
|
|
|
if( (textfont && textfont[0]) ||
|
|
|
|
|
(xctx->text[i].flags & (TEXT_BOLD | TEXT_OBLIQUE | TEXT_ITALIC))) {
|
|
|
|
|
cairo_font_slant_t slant;
|
|
|
|
|
cairo_font_weight_t weight;
|
|
|
|
|
textfont = (xctx->text[i].font && xctx->text[i].font[0]) ?
|
|
|
|
|
xctx->text[i].font : tclgetvar("cairo_font_name");
|
|
|
|
|
weight = ( xctx->text[i].flags & TEXT_BOLD) ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL;
|
|
|
|
|
slant = CAIRO_FONT_SLANT_NORMAL;
|
|
|
|
|
if(xctx->text[i].flags & TEXT_ITALIC) slant = CAIRO_FONT_SLANT_ITALIC;
|
|
|
|
|
if(xctx->text[i].flags & TEXT_OBLIQUE) slant = CAIRO_FONT_SLANT_OBLIQUE;
|
2022-11-07 13:34:48 +01:00
|
|
|
|
2025-04-06 12:58:13 +02:00
|
|
|
cairo_save(xctx->cairo_ctx);
|
|
|
|
|
cairo_save(xctx->cairo_save_ctx);
|
|
|
|
|
xctx->cairo_font =
|
|
|
|
|
cairo_toy_font_face_create(textfont, slant, weight);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font);
|
|
|
|
|
cairo_font_face_destroy(xctx->cairo_font);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
txt_ptr = get_text_floater(i);
|
|
|
|
|
dbg(1, "draw(): drawing string %d = %s\n",i, txt_ptr);
|
|
|
|
|
draw_string(textlayer, ADD, txt_ptr,
|
|
|
|
|
xctx->text[i].rot, xctx->text[i].flip, xctx->text[i].hcenter, xctx->text[i].vcenter,
|
|
|
|
|
xctx->text[i].x0,xctx->text[i].y0,
|
|
|
|
|
xctx->text[i].xscale, xctx->text[i].yscale);
|
|
|
|
|
#if HAS_CAIRO==1
|
|
|
|
|
if( (textfont && textfont[0]) ||
|
|
|
|
|
(xctx->text[i].flags & (TEXT_BOLD | TEXT_OBLIQUE | TEXT_ITALIC))) {
|
|
|
|
|
cairo_restore(xctx->cairo_ctx);
|
|
|
|
|
cairo_restore(xctx->cairo_save_ctx);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#if HAS_CAIRO!=1
|
2025-11-29 14:36:15 +01:00
|
|
|
drawrect(textlayer, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, -1, -1);
|
2025-11-29 00:47:46 +01:00
|
|
|
drawline(textlayer, END, 0.0, 0.0, 0.0, 0.0, 0.0, 0, NULL);
|
2025-04-06 12:58:13 +02:00
|
|
|
#endif
|
|
|
|
|
} /* for(i=0;i<xctx->texts; ++i) */
|
2022-11-07 13:34:48 +01:00
|
|
|
if(xctx->only_probes) build_colors(1.0, 0);
|
|
|
|
|
if(xctx->only_probes) {
|
|
|
|
|
xctx->save_lw = xctx->lw;
|
|
|
|
|
xctx->lw=3.0;
|
|
|
|
|
change_linewidth(xctx->lw);
|
|
|
|
|
}
|
2021-11-16 22:28:10 +01:00
|
|
|
draw_hilight_net(xctx->draw_window);
|
2022-11-07 13:34:48 +01:00
|
|
|
if(xctx->only_probes) {
|
|
|
|
|
xctx->lw = xctx->save_lw;
|
|
|
|
|
change_linewidth(xctx->save_lw);
|
|
|
|
|
}
|
2023-07-07 08:30:13 +02:00
|
|
|
/* do_copy_area is zero only when doing png hardcopy to avoid video flickering */
|
|
|
|
|
if(xctx->do_copy_area) {
|
2024-11-16 10:19:34 +01:00
|
|
|
if(!xctx->draw_window && xctx->draw_pixmap) {
|
2023-09-21 23:51:17 +02:00
|
|
|
MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], xctx->xrect[0].x, xctx->xrect[0].y,
|
2022-09-15 19:39:16 +02:00
|
|
|
xctx->xrect[0].width, xctx->xrect[0].height, xctx->xrect[0].x, xctx->xrect[0].y);
|
|
|
|
|
}
|
2023-04-09 15:36:06 +02:00
|
|
|
#if !defined(__unix__) && HAS_CAIRO==1
|
2022-09-15 19:39:16 +02:00
|
|
|
else
|
2022-11-30 00:59:45 +01:00
|
|
|
my_cairo_fill(xctx->cairo_sfc, xctx->xrect[0].x, xctx->xrect[0].y,
|
|
|
|
|
xctx->xrect[0].width, xctx->xrect[0].height);
|
2022-09-15 19:39:16 +02:00
|
|
|
#endif
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
2023-06-06 19:39:40 +02:00
|
|
|
if(tclgetboolvar("compare_sch") /* && xctx->sch_to_compare[0]*/ ){
|
2022-12-13 13:51:33 +01:00
|
|
|
compare_schematics("");
|
|
|
|
|
} else {
|
|
|
|
|
draw_selection(xctx->gc[SELLAYER], 0); /* 20181009 moved outside of cadlayers loop */
|
|
|
|
|
}
|
2025-01-27 01:49:06 +01:00
|
|
|
if(tclgetboolvar("draw_crosshair")) draw_crosshair(7, 0); /* what = 1(clear) + 2(draw) */
|
2022-11-07 13:34:48 +01:00
|
|
|
} /* if(has_x) */
|
2020-08-08 15:47:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef __unix__
|
|
|
|
|
/* place holder for Windows to show that these XLib functions are not supported in Windows. */
|
2020-09-30 23:55:07 +02:00
|
|
|
int XSetClipRectangles(register Display* dpy, GC gc, int clip_x_origin, int clip_y_origin,
|
|
|
|
|
XRectangle* rectangles, int n, int ordering)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-12-05 00:01:09 +01:00
|
|
|
int XSetTile(Display* display, GC gc, Pixmap s_pixmap)
|
2020-08-08 15:47:34 +02:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2022-03-01 19:36:08 +01:00
|
|
|
|
2022-09-15 19:39:16 +02:00
|
|
|
void MyXCopyArea(Display* display, Drawable src, Drawable dest, GC gc, int src_x, int src_y,
|
|
|
|
|
unsigned int width, unsigned int height, int dest_x, int dest_y)
|
2022-03-01 19:36:08 +01:00
|
|
|
{
|
2023-10-27 10:48:53 +02:00
|
|
|
dbg(1, "MyXCopyArea(%d, %d, %u, %u)\n", src_x, src_y, width, height);
|
2022-12-02 16:43:01 +01:00
|
|
|
#if !defined(__unix__)
|
|
|
|
|
XCopyArea(display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
|
2023-04-09 15:36:06 +02:00
|
|
|
#if HAS_CAIRO==1
|
2022-03-01 19:36:08 +01:00
|
|
|
my_cairo_fill(xctx->cairo_save_sfc, dest_x, dest_y, width, height);
|
2022-12-02 16:43:01 +01:00
|
|
|
#endif
|
2023-09-23 01:46:39 +02:00
|
|
|
/*
|
|
|
|
|
* #elif (defined(__unix__) && HAS_CAIRO==1) || DRAW_ALL_CAIRO==1
|
|
|
|
|
* cairo_set_source_surface(xctx->cairo_ctx, xctx->cairo_save_sfc, 0, 0);
|
|
|
|
|
* cairo_paint(xctx->cairo_ctx);
|
|
|
|
|
*/
|
2022-11-30 00:59:45 +01:00
|
|
|
#else
|
|
|
|
|
XCopyArea(display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
|
|
|
|
|
#endif
|
2022-03-01 19:36:08 +01:00
|
|
|
}
|
2023-09-23 01:46:39 +02:00
|
|
|
|
|
|
|
|
void MyXCopyAreaDouble(Display* display, Drawable src, Drawable dest, GC gc,
|
|
|
|
|
double sx1, double sy1, double sx2, double sy2,
|
|
|
|
|
double dx1, double dy1, double lw)
|
|
|
|
|
{
|
|
|
|
|
double isx1, isy1, isx2, isy2, idx1, idy1;
|
|
|
|
|
unsigned int width, height;
|
2025-05-01 23:52:53 +02:00
|
|
|
int intlw = INT_LINE_W(lw);
|
2024-03-02 02:29:03 +01:00
|
|
|
dbg(1, "MyXCopyAreaDouble(%g, %g, %g, %g, intlw=%d)\n", sx1, sy1, sx2, sy2, intlw);
|
2023-11-03 14:36:35 +01:00
|
|
|
isx1=X_TO_SCREEN(sx1) - 2 * intlw;
|
|
|
|
|
isy1=Y_TO_SCREEN(sy1) - 2 * intlw;
|
|
|
|
|
isx2=X_TO_SCREEN(sx2) + 2 * intlw;
|
|
|
|
|
isy2=Y_TO_SCREEN(sy2) + 2 * intlw;
|
2023-09-23 01:46:39 +02:00
|
|
|
|
2023-11-03 14:36:35 +01:00
|
|
|
idx1=X_TO_SCREEN(dx1) - 2 * intlw;
|
|
|
|
|
idy1=Y_TO_SCREEN(dy1) - 2 * intlw;
|
2023-09-23 01:46:39 +02:00
|
|
|
|
|
|
|
|
width = (unsigned int)isx2 - (unsigned int)isx1;
|
|
|
|
|
height = (unsigned int)isy2 - (unsigned int)isy1;
|
|
|
|
|
#if !defined(__unix__)
|
|
|
|
|
XCopyArea(display, src, dest, gc, (int)isx1, (int)isy1, width, height, (int)idx1, (int)idy1);
|
|
|
|
|
#if HAS_CAIRO==1
|
2023-10-11 13:59:08 +02:00
|
|
|
my_cairo_fill(xctx->cairo_save_sfc, (int)idx1, (int)idy1, width, height);
|
2023-09-23 01:46:39 +02:00
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
XCopyArea(display, src, dest, gc, (int)isx1, (int)isy1, width, height, (int)idx1, (int)idy1);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|