/* * COPYRIGHT * * cschem - modular/flexible schematics editor - libcschem (core library) * Copyright (C) 2018,2022 Tibor 'Igor2' Palinkas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA * * Contact: * Project page: http://repo.hu/projects/sch-rnd * contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html * mailing list: http://www.repo.hu/projects/sch-rnd/contact.html */ #include "config.h" #include #include #include #include "event.h" #include "concrete.h" #include "cnc_arc.h" #include "cnc_obj.h" #include "cnc_grp_child.h" #include "op_common.h" #include "operation.h" #include "undo.h" #include "rotate.h" #include "gengeo2d/sarc.h" #include "gengeo2d/xform.h" csch_arc_t *csch_arc_alloc(csch_sheet_t *sheet, csch_cgrp_t *parent, csch_oid_t oid) { csch_arc_t *arc; arc = htip_get(&parent->id2obj, oid); if (arc != NULL) return NULL; arc = calloc(sizeof(csch_arc_t), 1); csch_cobj_init(&arc->hdr, sheet, parent, oid, CSCH_CTYPE_ARC); return arc; } csch_arc_t *csch_arc_dup(csch_sheet_t *sheet, csch_cgrp_t *parent, const csch_arc_t *src, int keep_id) { csch_arc_t *dst = csch_arc_alloc(sheet, parent, CSCH_KEEP_OID(parent, src->hdr.oid)); dst->inst = src->inst; dst->spec = src->spec; csch_chdr_copy_meta4dup(&dst->hdr, &src->hdr); return dst; } void csch_arc_free(csch_arc_t *arc) { csch_cobj_uninit(&arc->hdr); free(arc); } csch_arc_t *csch_arc_get(csch_sheet_t *sheet, csch_cgrp_t *grp, csch_oid_t oid) { csch_arc_t *arc = htip_get(&grp->id2obj, oid); if ((arc != NULL) && (arc->hdr.type != CSCH_CTYPE_ARC)) return NULL; return arc; } void csch_arc_update_xform(csch_sheet_t *sheet, csch_arc_t *arc) { arc->inst.c = arc->spec; /* copies radius and angles */ if (arc->hdr.parent != NULL) { csch_coord_t centx, centy; double start, delta, rot; csch_child_xform_t *cx; g2d_xform_t mx = csch_grp_ref_parent_mx(&arc->hdr, &cx); arc->inst.c.c = g2d_xform_vect2vect(mx, arc->spec.c); csch_arc_mirror_(arc, arc->hdr.parent->xform.x + cx->movex, arc->hdr.parent->xform.y + cx->movey, arc->hdr.parent->xform.mirx ^ cx->mirx, arc->hdr.parent->xform.miry ^ cx->miry, ¢x, ¢y, &start, &delta); arc->inst.c.start = start; arc->inst.c.delta = delta; rot = (arc->hdr.parent->xform.rot + cx->rot) / RND_RAD_TO_DEG; arc->inst.c.start += fmod(rot, 2.0*G2D_PI); } else arc->inst.c = arc->spec; if (arc->hdr.stroke != NULL) { arc->inst.s.width = arc->hdr.stroke->size; arc->inst.s.cap = (arc->hdr.stroke->shape == CSCH_PSHP_ROUND) ? G2D_CAP_ROUND : G2D_CAP_SQUARE; } } void csch_arc_update_bbox(csch_sheet_t *sheet, csch_arc_t *arc) { g2d_box_t gb; csch_obj_bbox_reset(arc); gb = g2d_sarc_bbox(&arc->inst); arc->hdr.bbox.x1 = gb.p1.x; arc->hdr.bbox.y1 = gb.p1.y; arc->hdr.bbox.x2 = gb.p2.x; arc->hdr.bbox.y2 = gb.p2.y; } void csch_arc_center_bbox(csch_sheet_t *sheet, const csch_arc_t *arc, csch_rtree_box_t *dst) { g2d_box_t gb = g2d_carc_bbox(&arc->inst.c); dst->x1 = gb.p1.x; dst->y1 = gb.p1.y; dst->x2 = gb.p2.x; dst->y2 = gb.p2.y; } void csch_arc_update(csch_sheet_t *sheet, csch_arc_t *arc, int do_xform) { csch_cobj_update_pen(&arc->hdr); if (do_xform) csch_arc_update_xform(sheet, arc); csch_cobj_rtree_del(sheet, &arc->hdr); csch_arc_update_bbox(sheet, arc); csch_cobj_rtree_add(sheet, &arc->hdr); csch_cobj_bbox_changed(sheet, &arc->hdr); } int csch_arc_get_endxy(const csch_arc_t *arc, int side, csch_coord_t *x, csch_coord_t *y) { g2d_vect_t v = g2d_carc_offs(&arc->inst.c, ((side == 0) ? 0.0 : 1.0)); *x = v.x; *y = v.y; return 0; } int csch_arc_get_midxy(const csch_arc_t *arc, csch_coord_t *x, csch_coord_t *y) { g2d_vect_t v = g2d_carc_offs(&arc->inst.c, 0.5); *x = v.x; *y = v.y; return 0; } int csch_arc_get_spec_endxy(const csch_arc_t *arc, int side, csch_coord_t *x, csch_coord_t *y) { g2d_vect_t v = g2d_carc_offs(&arc->spec, ((side == 0) ? 0.0 : 1.0)); *x = v.x; *y = v.y; return 0; } int csch_arc_get_spec_midxy(const csch_arc_t *arc, csch_coord_t *x, csch_coord_t *y) { g2d_vect_t v = g2d_carc_offs(&arc->spec, 0.5); *x = v.x; *y = v.y; return 0; } static csch_chdr_t *arc_create(csch_sheet_t *sheet, csch_cgrp_t *parent) { csch_arc_t *arc = csch_arc_alloc(sheet, parent, csch_oid_new(sheet, parent)); if (arc == NULL) return NULL; return &arc->hdr; } static void arc_remove_alloc(csch_undo_remove_t *slot) { } static void arc_remove_redo(csch_undo_remove_t *slot) { csch_cnc_common_remove_redo(slot, CSCH_REM_FROM_RTREE | CSCH_REM_FROM_PARENT | CSCH_REM_DEL_EMPTY_PARENT); } static void arc_remove_undo(csch_undo_remove_t *slot) { csch_cnc_common_remove_undo(slot, CSCH_REM_FROM_RTREE | CSCH_REM_FROM_PARENT | CSCH_REM_DEL_EMPTY_PARENT); } static int arc_isc_with_box(csch_chdr_t *obj, csch_rtree_box_t *box) { csch_arc_t *arc = (csch_arc_t *)obj; g2d_box_t gbox, abox; g2d_carc_t outer, inner; g2d_vect_t tmp1[8]; g2d_offs_t tmp2[8]; gbox.p1.x = box->x1; gbox.p1.y = box->y1; gbox.p2.x = box->x2; gbox.p2.y = box->y2; abox.p1.x = arc->hdr.bbox.x1; abox.p1.y = arc->hdr.bbox.y1; abox.p2.x = arc->hdr.bbox.x2; abox.p2.y = arc->hdr.bbox.y2; /* arc fully within the box (cheap) */ if (g2d_box_in_box(&gbox, &abox)) return 1; g2d_sarc_sides(&arc->inst, &outer, &inner); /* check if outer arc is hit by the box; this is the most common case: if the box crosses the arc, it surely hits outer arc */ if (g2d_iscp_carc_box(&outer, &gbox, tmp1, tmp2)) return 1; /* special cases, cheaper first */ TODO("check end cap circle"); /* the corner of a small box within the sarc may hit the inner arc... */ return g2d_iscp_carc_box(&inner, &gbox, tmp1, tmp2); } static void arc_move(csch_sheet_t *sheet, csch_chdr_t *obj, csch_coord_t dx, csch_coord_t dy, int undoable) { csch_arc_t *arc = (csch_arc_t *)obj; csch_arc_modify(sheet, arc, &dx, &dy, NULL, NULL, NULL, undoable, 1); } static void arc_copy(csch_sheet_t *sheet, csch_chdr_t *obj, csch_coord_t dx, csch_coord_t dy, int undoable) { csch_arc_t *arc = csch_arc_dup(sheet, obj->parent, (csch_arc_t *)obj, 0); if (arc != NULL) { arc->spec.c.x += dx; arc->spec.c.y += dy; csch_arc_update(sheet, arc, 1); csch_op_inserted(sheet, obj->parent, &arc->hdr); } } void csch_arc_rotate_(csch_arc_t *arc, csch_coord_t rcx, csch_coord_t rcy, double da, double cs, double sn, csch_coord_t *cx_out, csch_coord_t *cy_out, double *start_out) { csch_coord_t ncx = arc->spec.c.x, ncy = arc->spec.c.y; csch_rotate_pt(&ncx, &ncy, rcx, rcy, cs, sn); *cx_out = ncx; *cy_out = ncy; *start_out = fmod(arc->spec.start + da / RND_RAD_TO_DEG, 2.0*G2D_PI); } static void arc_rotate(csch_sheet_t *sheet, csch_chdr_t *obj, csch_coord_t rcx, csch_coord_t rcy, double da, int undoable) { csch_arc_t *arc = (csch_arc_t *)obj; double rad = -da / RND_RAD_TO_DEG; csch_coord_t ncx, ncy; double nstart; csch_arc_rotate_(arc, rcx, rcy, da, cos(rad), sin(rad), &ncx, &ncy, &nstart); csch_arc_modify(sheet, arc, &ncx, &ncy, NULL, &nstart, NULL, undoable, 0); } void csch_arc_rotate90_(csch_arc_t *arc, csch_coord_t rcx, csch_coord_t rcy, int n, csch_coord_t *cx_out, csch_coord_t *cy_out, double *start_out) { csch_coord_t ncx = arc->spec.c.x, ncy = arc->spec.c.y; csch_rotate90_pt(&ncx, &ncy, rcx, rcy, n); *cx_out = ncx; *cy_out = ncy; *start_out = fmod(arc->spec.start + (double)n * G2D_PI/2.0, 2.0*G2D_PI); } static void arc_rotate90(csch_sheet_t *sheet, csch_chdr_t *obj, csch_coord_t rcx, csch_coord_t rcy, int n, int undoable) { csch_arc_t *arc = (csch_arc_t *)obj; csch_coord_t ncx, ncy; double nstart; csch_arc_rotate90_(arc, rcx, rcy, n, &ncx, &ncy, &nstart); csch_arc_modify(sheet, arc, &ncx, &ncy, NULL, &nstart, NULL, undoable, 0); } void csch_arc_mirror_(csch_arc_t *arc, csch_coord_t mcx, csch_coord_t mcy, int mirx, int miry, csch_coord_t *cx_out, csch_coord_t *cy_out, double *start_out, double *delta_out) { csch_coord_t ncx = arc->spec.c.x, ncy = arc->spec.c.y; double nstart = arc->spec.start, ndelta = arc->spec.delta, nend = nstart+ndelta; double sdx = cos(nstart), sdy = sin(nstart), edx = cos(nend), edy = sin(nend); int delta_pos = (ndelta > 0); int delta_full = (ndelta >= G2D_PI * 2.0) || (ndelta <= -(G2D_PI * 2.0)); csch_mirror_pt(&ncx, &ncy, mcx, mcy, mirx, miry); if (mirx) { sdx = -sdx; edx = -edx; delta_pos = !delta_pos; } if (miry) { sdy = -sdy; edy = -edy; delta_pos = !delta_pos; } nstart = atan2(sdy, sdx); if (!delta_full) { nend = atan2(edy, edx); ndelta = nend - nstart; if (delta_pos && (ndelta < 0)) ndelta += G2D_PI * 2.0; if (!delta_pos && (ndelta > 0)) ndelta -= G2D_PI * 2.0; } else ndelta = G2D_PI * 2.0; *cx_out = ncx; *cy_out = ncy; *start_out = nstart; *delta_out = ndelta; } static void arc_mirror(csch_sheet_t *sheet, csch_chdr_t *obj, csch_coord_t mcx, csch_coord_t mcy, int mirx, int miry, int undoable) { csch_arc_t *arc = (csch_arc_t *)obj; csch_coord_t ncx, ncy; double nstart, ndelta; csch_arc_mirror_(arc, mcx, mcy, mirx, miry, &ncx, &ncy, &nstart, &ndelta); csch_arc_modify(sheet, arc, &ncx, &ncy, NULL, &nstart, &ndelta, undoable, 0); } static void arc_inst2spec(csch_sheet_t *sheet, csch_chdr_t *obj, const csch_chdr_t *in, int undoable) { csch_arc_t *arc = (csch_arc_t *)in; csch_coord_t cx = arc->inst.c.c.x, cy = arc->inst.c.c.y, r = arc->inst.c.r; double start = arc->inst.c.start, delta = arc->inst.c.delta; csch_arc_modify(sheet, (csch_arc_t *)obj, &cx, &cy, &r, &start, &delta, undoable, 0); } const csch_ops_t csch_ops_arc = { arc_create, arc_remove_alloc, arc_remove_redo, arc_remove_undo, arc_isc_with_box, arc_move, arc_copy, arc_rotate, arc_rotate90, arc_mirror, arc_inst2spec }; /*** Hash ***/ unsigned csch_arc_hash_(const csch_arc_t *arc, csch_hash_ignore_t ignore, int in_contour) { unsigned res = 0; if (!in_contour) res ^= csch_chdr_hash(&arc->hdr); if (!(ignore & CSCH_HIGN_FLOATER_GEO) || !arc->hdr.floater) { res ^= csch_coord_hash(arc->spec.c.x); res ^= csch_coord_hash(arc->spec.c.y); /* angles change with rotation, thus they are part of the floater set */ res ^= csch_angle_hash(arc->spec.start); res ^= csch_angle_hash(arc->spec.delta); } res ^= csch_coord_hash(arc->spec.r); return res; } unsigned csch_arc_hash(const csch_arc_t *arc, csch_hash_ignore_t ignore) { return csch_arc_hash_(arc, ignore, 0); } int csch_arc_keyeq_(const csch_arc_t *a1, const csch_arc_t *a2, csch_hash_ignore_t ignore, int in_contour) { if (!in_contour && !csch_chdr_eq(&a1->hdr, &a2->hdr)) return 0; if (!(ignore & CSCH_HIGN_FLOATER_GEO) || !a1->hdr.floater || !a2->hdr.floater) { if (a1->spec.c.x != a2->spec.c.x) return 0; if (a1->spec.c.y != a2->spec.c.y) return 0; /* angles change with rotation, thus they are part of the floater set */ if (!csch_angle_eq(a1->spec.start, a2->spec.start)) return 0; if (!csch_angle_eq(a1->spec.delta, a2->spec.delta)) return 0; } if (a1->spec.r != a2->spec.r) return 0; return 1; } int csch_arc_keyeq(const csch_arc_t *a1, const csch_arc_t *a2, csch_hash_ignore_t ignore) { return csch_arc_keyeq_(a1, a2, ignore, 0); } /*** Modify ***/ typedef struct { csch_arc_t *arc; /* it is safe to save the object pointer because it is persistent (through the removed object list) */ g2d_carc_t spec; } undo_arc_modify_t; static int undo_arc_modify_swap(void *udata) { undo_arc_modify_t *u = udata; csch_cobj_redraw(u->arc); rnd_swap(g2d_carc_t, u->spec, u->arc->spec); csch_arc_update(u->arc->hdr.sheet, u->arc, 1); csch_op_geo_edited(u->arc->hdr.sheet, &u->arc->hdr); return 0; } static void undo_arc_modify_print(void *udata, char *dst, size_t dst_len) { rnd_snprintf(dst, dst_len, "arc geometry change"); } static const char core_arc_cookie[] = "libcschem/core/cnc_arc.c"; static const uundo_oper_t undo_arc_modify = { core_arc_cookie, NULL, undo_arc_modify_swap, undo_arc_modify_swap, undo_arc_modify_print }; void csch_arc_modify(csch_sheet_t *sheet, csch_arc_t *arc, csch_coord_t *cx, csch_coord_t *cy, csch_coord_t *r, double *start, double *delta, int undoable, int relative) { undo_arc_modify_t utmp, *u = &utmp; if (undoable) u = uundo_append(&sheet->undo, &undo_arc_modify, sizeof(undo_arc_modify_t)); u->arc = arc; u->spec = arc->spec; /* copy all fields */ u->spec.c.x = (cx == NULL) ? arc->spec.c.x : (relative ? arc->spec.c.x + *cx : *cx); u->spec.c.y = (cy == NULL) ? arc->spec.c.y : (relative ? arc->spec.c.y + *cy : *cy); u->spec.r = (r == NULL) ? arc->spec.r : (relative ? arc->spec.r + *r : *r); u->spec.start = (start == NULL) ? arc->spec.start : (relative ? arc->spec.start + *start : *start); u->spec.delta = (delta == NULL) ? arc->spec.delta : (relative ? arc->spec.delta + *delta : *delta); undo_arc_modify_swap(u); if (undoable) csch_undo_inc_serial(sheet); }