/* * Copyright (c) 2005-2015 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include void RG_SketchInit(RG_Sketch *sk, RG_Tileset *ts, int flags) { sk->name[0] = '\0'; sk->flags = flags; sk->ts = ts; sk->h = 0.0; sk->s = 0.0; sk->v = 0.0; sk->a = 1.0; sk->vg = Malloc(sizeof(VG)); VG_Init(sk->vg, VG_ANTIALIAS|VG_ALPHA); sk->ublks = Malloc(sizeof(RG_SketchUndoBlk)); sk->nublks = 1; sk->curblk = 0; RG_SketchBeginUndoBlk(sk); sk->ublks[0].mods = Malloc(sizeof(RG_SketchMod)); sk->ublks[0].nmods = 0; } void RG_SketchScale(RG_Sketch *sk, int w, int h, float scale, int x, int y) { VG *vg = sk->vg; #if 0 float xoffs = (float)x/(float)RG_TILESZ/scale; float yoffs = (float)y/(float)RG_TILESZ/scale; #endif AG_Rect r; r.w = (w == -1) ? vg->rDst.w : w; r.h = (h == -1) ? vg->rDst.h : h; VG_Scale(vg, r.w, r.h, scale*RG_TILESZ); #if 0 TAILQ_FOREACH(vge, &vg->vges, vges) { for (i = 0; i < vge->nvtx; i++) { vge->vtx[i].x += xoffs; vge->vtx[i].y += yoffs; } } #endif } void RG_SketchDestroy(RG_Sketch *sk) { int i; VG_Destroy(sk->vg); for (i = 0; i < sk->nublks; i++) { Free(sk->ublks[i].mods); } Free(sk->ublks); } int RG_SketchLoad(RG_Sketch *sk, AG_DataSource *buf) { int vgflags; AG_CopyString(sk->name, buf, sizeof(sk->name)); sk->flags = (int)AG_ReadUint32(buf); vgflags = (int)AG_ReadUint32(buf); sk->vg = VG_New(vgflags); if (VG_Load(sk->vg, buf) == -1) { VG_Destroy(sk->vg); Free(sk->vg); return (-1); } VG_Rasterize(sk->vg); return (0); } void RG_SketchSave(RG_Sketch *sk, AG_DataSource *buf) { AG_WriteString(buf, sk->name); AG_WriteUint32(buf, (Uint32)sk->flags); AG_WriteUint32(buf, (Uint32)sk->vg->flags); VG_Save(sk->vg, buf); } void RG_SketchRender(RG_Tile *t, RG_TileElement *tel) { RG_Sketch *sk = tel->tel_sketch.sk; VG *vg = sk->vg; VG_Element *vge; AG_Rect rd; AG_FillRect(vg->su, NULL, vg->fillColor); if (vg->flags & VG_VISGRID) VG_DrawGrid(vg); #ifdef SG_DEBUG if (vg->flags & VG_VISBBOXES) VG_DrawExtents(vg); #endif TAILQ_FOREACH(vge, &vg->vges, vges) { switch (vge->type) { case VG_POLYGON: RG_SketchDrawPolygon(t, vg, vge); break; default: VG_Rasterize(vg); break; } } if (vg->flags & VG_VISORIGIN) VG_DrawOrigin(vg); rd.x = tel->tel_sketch.x; rd.y = tel->tel_sketch.y; rd.w = vg->su->w; rd.h = vg->su->h; t->blend_fn(t, vg->su, &rd); } static void UpdateScale(AG_Event *event) { RG_Tileview *tv = AG_PTR(1); RG_TileElement *tel = AG_PTR(2); RG_Sketch *sk = tel->tel_sketch.sk; RG_SketchScale(sk, -1, -1, tel->tel_sketch.scale, 0, 0); tv->tile->flags |= RG_TILE_DIRTY; } AG_Window * RG_SketchEdit(RG_Tileview *tv, RG_TileElement *tel) { RG_Sketch *sk = tel->tel_sketch.sk; AG_Window *win; AG_Notebook *nb; AG_NotebookTab *ntab; win = AG_WindowNew(0); AG_WindowSetCaption(win, _("Sketch %s"), sk->name); AG_WindowSetPosition(win, AG_WINDOW_MIDDLE_LEFT, 0); nb = AG_NotebookNew(win, AG_NOTEBOOK_HFILL|AG_NOTEBOOK_VFILL); ntab = AG_NotebookAdd(nb, _("Color"), AG_BOX_VERT); { AG_HSVPal *pal; pal = AG_HSVPalNew(ntab, AG_HSVPAL_EXPAND); AG_BindPointer(pal, "pixel-format", &tv->ts->fmt); AG_BindFloat(pal, "hue", &sk->h); AG_BindFloat(pal, "saturation", &sk->s); AG_BindFloat(pal, "value", &sk->v); AG_BindFloat(pal, "alpha", &sk->a); } ntab = AG_NotebookAdd(nb, _("Texture"), AG_BOX_VERT); ntab = AG_NotebookAdd(nb, _("Settings"), AG_BOX_VERT); { AG_MSpinbutton *msb; AG_Numerical *num; num = AG_NumericalNewFltR(ntab, 0, NULL, _("Scale: "), &tel->tel_sketch.scale, 1e-4, 1e4); AG_NumericalSetIncrement(num, 0.1); AG_SetEvent(num, "numerical-changed", UpdateScale, "%p,%p", tv, tel); msb = AG_MSpinbuttonNew(ntab, 0, ",", _("Coordinates: ")); AG_BindInt(msb, "xvalue", &tel->tel_sketch.x); AG_BindInt(msb, "yvalue", &tel->tel_sketch.y); AG_SetEvent(num, "numerical-changed", UpdateScale, "%p,%p", tv, tel); AG_NumericalNewIntR(ntab, 0, NULL, _("Overall alpha: "), &tel->tel_sketch.alpha, 0, 255); } return (win); } static void PollStyles(AG_Event *event) { AG_Tlist *tl = AG_SELF(); VG *vg = AG_PTR(1); VG_Style *vgs; AG_TlistClear(tl); AG_MutexLock(&vg->lock); TAILQ_FOREACH(vgs, &vg->styles, styles) { AG_TlistAddPtr(tl, NULL, vgs->name, vgs); } AG_MutexUnlock(&vg->lock); AG_TlistRestore(tl); } AG_Window * RG_SketchEditElement(RG_Tileview *tv, RG_TileElement *tel, VG_Element *vge) { RG_Sketch *sk = tel->tel_sketch.sk; VG *vg = sk->vg; AG_Window *win; AG_Notebook *nb; AG_NotebookTab *ntab; const VG_ElementOps *ops; AG_Combo *com; AG_Label *lbl; if ((win = AG_WindowNewNamed(0, "%s-%p-%p", sk->name, tel, vge)) == NULL) { return (NULL); } AG_WindowSetCaption(win, _("Sketch element (%s)"), sk->name); AG_WindowSetPosition(win, AG_WINDOW_MIDDLE_RIGHT, 0); ops = vgElementTypes[vge->type]; AG_LabelNew(win, 0, _("Element type: %s"), ops->name); lbl = AG_LabelNewPolled(win, AG_LABEL_HFILL, _("Vertices: %u"), &vge->nvtx); AG_LabelSizeHint(lbl, 1, _("Vertices: 00000000")); AG_NumericalNewIntR(win, 0, NULL, _("Layer: "), &vge->layer, 0, 255); com = AG_ComboNew(win, AG_COMBO_POLL|AG_COMBO_HFILL, _("Style: ")); AG_SetEvent(com->list, "tlist-poll", PollStyles, "%p", vg); nb = AG_NotebookNew(win, AG_NOTEBOOK_HFILL|AG_NOTEBOOK_VFILL); ntab = AG_NotebookAdd(nb, _("Color"), AG_BOX_VERT); { AG_HSVPal *pal; pal = AG_HSVPalNew(ntab, AG_HSVPAL_EXPAND); AG_BindPointer(pal, "pixel-format", &sk->vg->fmt); AG_BindUint32(pal, "pixel", &vge->color); } ntab = AG_NotebookAdd(nb, _("Line style"), AG_BOX_VERT); { const char *line_styles[] = { N_("Continuous"), N_("Stippled"), NULL }; const char *endpoint_styles[] = { N_("Square"), N_("Beveled"), N_("Rounded"), N_("Mitered"), NULL }; AG_LabelNewS(ntab, 0, _("Line style: ")); AG_RadioNewUint(ntab, AG_RADIO_HFILL, line_styles, &vge->line_st.style); AG_LabelNewS(ntab, 0, _("Endpoint style: ")); AG_RadioNewUint(ntab, AG_RADIO_HFILL, endpoint_styles, &vge->line_st.endpoint_style); AG_NumericalNewUint16(ntab, 0, NULL, _("Stipple pattern: "), &vge->line_st.stipple); AG_NumericalNewUint8R(ntab, 0, "px", _("Thickness: "), &vge->line_st.thickness, 1, 20); AG_NumericalNewUint8R(ntab, 0, "px", _("Miter length: "), &vge->line_st.miter_len, 1, 20); } ntab = AG_NotebookAdd(nb, _("Filling style"), AG_BOX_VERT); { const char *fill_styles[] = { N_("None"), N_("Solid"), N_("Textured"), NULL }; RG_TextureSelector *texsel; AG_LabelNewS(ntab, 0, _("Filling style: ")); AG_RadioNewUint(ntab, AG_RADIO_HFILL, fill_styles, &vge->fill_st.style); AG_LabelNewS(ntab, 0, _("Texture: ")); texsel = RG_TextureSelectorNew(ntab, tv->ts, 0); WIDGET(texsel)->flags |= AG_WIDGET_HFILL|AG_WIDGET_VFILL; AG_BindString(texsel, "texture-name", vge->fill_st.texture, sizeof(vge->fill_st.texture)); } return (win); } void RG_SketchBeginUndoBlk(RG_Sketch *sk) { RG_SketchUndoBlk *ublk; while (sk->nublks > sk->curblk+1) { ublk = &sk->ublks[sk->nublks-1]; Free(ublk->mods); sk->nublks--; } sk->ublks = Realloc(sk->ublks, ++sk->nublks*sizeof(RG_SketchUndoBlk)); sk->curblk++; ublk = &sk->ublks[sk->curblk]; ublk->mods = Malloc(sizeof(RG_SketchMod)); ublk->nmods = 0; } void RG_SketchUndo(RG_Tileview *tv, RG_TileElement *tel) { RG_Sketch *sk = tel->tel_sketch.sk; /* RG_SketchUndoBlk *ublk = &sk->ublks[sk->curblk]; */ /* int i; */ if (sk->curblk-1 <= 0) return; #if 0 for (i = 0; i < ublk->nmods; i++) { RG_SketchMod *mod = &ublk->mods[i]; } #endif sk->curblk--; tv->tile->flags |= RG_TILE_DIRTY; } void RG_SketchRedo(RG_Tileview *tv, RG_TileElement *tel) { /* TODO */ } static void UpdateCircleRadius(AG_Event *event) { VG_Element *vge = AG_PTR(2); vge->vg_circle.radius = Sqrt( pow(vge->vtx[1].x - vge->vtx[0].x, 2) + pow(vge->vtx[1].y - vge->vtx[0].y, 2)); } static void UpdateCircleVertex(AG_Event *event) { VG_Element *vge = AG_PTR(2); vge->vtx[1].x = vge->vtx[0].x + vge->vg_circle.radius; vge->vtx[1].y = vge->vtx[0].y; } AG_Window * RG_SketchSelect(RG_Tileview *tv, RG_TileElement *tel, VG_Element *vge) { RG_Sketch *sk = tel->tel_sketch.sk; VG *vg = sk->vg; RG_TileviewCtrl *ctrl; Uint i; vge->selected = 1; switch (vge->type) { case VG_LINES: case VG_LINE_STRIP: case VG_LINE_LOOP: case VG_POLYGON: for (i = 0; i < vge->nvtx; i++) { ctrl = RG_TileviewAddCtrl(tv, RG_TILEVIEW_VERTEX, "%*d,%*d", &vge->vtx[i].x, &vge->vtx[i].y); ctrl->vg = vg; ctrl->vge = vge; ctrl->buttonup = NULL; } break; case VG_CIRCLE: ctrl = RG_TileviewAddCtrl(tv, RG_TILEVIEW_VERTEX, "%*d,%*d", &vge->vtx[0].x, &vge->vtx[0].y); ctrl->vg = vg; ctrl->vge = vge; ctrl->buttonup = NULL; ctrl->motion = AG_SetEvent(tv, NULL, UpdateCircleVertex, "%p,%p", vg, vge); ctrl = RG_TileviewAddCtrl(tv, RG_TILEVIEW_VERTEX, "%*d,%*d", &vge->vtx[1].x, &vge->vtx[1].y); ctrl->vg = vg; ctrl->vge = vge; ctrl->buttonup = NULL; ctrl->motion = AG_SetEvent(tv, NULL, UpdateCircleRadius, "%p,%p", vg, vge); break; default: break; } return (RG_SketchEditElement(tv, tel, vge)); } void RG_SketchUnselect(RG_Tileview *tv, RG_TileElement *tel, VG_Element *vge) { char name[AG_OBJECT_NAME_MAX]; RG_Sketch *sk = tel->tel_sketch.sk; VG *vg = sk->vg; RG_TileviewCtrl *ctrl, *nctrl; AG_Window *win; AG_Driver *drv = WIDGET(tv)->drv; /* XXX odd way to handle this */ TAILQ_FOREACH(win, &drv->visible, windows) { Snprintf(name, sizeof(name), "win-%s-%p-%p", sk->name, tel, vge); if (strcmp(name, OBJECT(win)->name) == 0) { AG_ObjectDetach(win); break; } } for (ctrl = TAILQ_FIRST(&tv->ctrls); ctrl != TAILQ_END(&tv->ctrls); ctrl = nctrl) { nctrl = TAILQ_NEXT(ctrl, ctrls); if (ctrl->vg == vg && ctrl->vge == vge) RG_TileviewDelCtrl(tv, ctrl); } vge->selected = 0; } void RG_SketchButtondown(RG_Tileview *tv, RG_TileElement *tel, float x, float y, int button) { RG_Sketch *sk = tel->tel_sketch.sk; VG *vg = sk->vg; if (button == AG_MOUSE_MIDDLE) { int x, y; AG_MouseGetState(WIDGET(tv)->drv->mouse, &x, &y); RG_SketchOpenMenu(tv, x, y); return; } else if (button == AG_MOUSE_RIGHT) { if (tv->cur_tool == NULL || (tv->cur_tool->flags & TILEVIEW_SKETCH_TOOL) == 0) { tv->scrolling++; return; } } if (tv->cur_tool != NULL && tv->cur_tool->flags & TILEVIEW_SKETCH_TOOL) { const RG_TileviewSketchToolOps *ops = (const RG_TileviewSketchToolOps *)tv->cur_tool->ops; if (ops->mousebuttondown != NULL) { ops->mousebuttondown(tv->cur_tool, sk, x, y, button); return; } } { VG_Element *vge; float idx, closest_idx = AG_FLT_MAX; VG_Element *closest_vge = NULL; TAILQ_FOREACH(vge, &vg->vges, vges) { if (vge->ops->intsect != NULL) { float ix = (float)x; float iy = (float)y; idx = vge->ops->intsect(vg, vge, &ix, &iy); if (idx < closest_idx) { closest_idx = idx; closest_vge = vge; } } } if (closest_vge != NULL && closest_idx < AG_FLT_MAX-2) { if (closest_vge->selected) { RG_SketchUnselect(tv, tel, closest_vge); } else { AG_Window *pwin = AG_ParentWindow(tv); AG_Window *win; if (!(AG_GetModState(tv) & AG_KEYMOD_CTRL)) { TAILQ_FOREACH(vge, &vg->vges, vges) { if (vge->selected) RG_SketchUnselect(tv, tel, vge); } } win = RG_SketchSelect(tv, tel, closest_vge); if (win != NULL) { AG_WindowAttach(pwin, win); AG_WindowShow(win); AG_WidgetFocus(tv); AG_WindowFocus(win); } } } } } void RG_SketchButtonup(RG_Tileview *tv, RG_TileElement *tel, float x, float y, int button) { if (tv->cur_tool != NULL && tv->cur_tool->flags & TILEVIEW_SKETCH_TOOL) { const RG_TileviewSketchToolOps *ops = (const RG_TileviewSketchToolOps *)tv->cur_tool->ops; if (ops->mousebuttonup != NULL) ops->mousebuttonup(tv->cur_tool, tel->tel_sketch.sk, x, y, button); } } void RG_SketchMotion(RG_Tileview *tv, RG_TileElement *tel, float x, float y, float xrel, float yrel, int state) { if (tv->cur_tool != NULL && tv->cur_tool->flags & TILEVIEW_SKETCH_TOOL) { const RG_TileviewSketchToolOps *ops = (const RG_TileviewSketchToolOps *)tv->cur_tool->ops; if (ops->mousemotion != NULL) { ops->mousemotion(tv->cur_tool, tel->tel_sketch.sk, x, y, xrel, yrel); return; } } { RG_Sketch *sk = tel->tel_sketch.sk; VG *vg = sk->vg; float idx, closest_idx = AG_FLT_MAX; VG_Element *vge, *closest_vge = NULL; TAILQ_FOREACH(vge, &vg->vges, vges) { vge->mouseover = 0; if (vge->ops->intsect != NULL) { float ix = (float)x; float iy = (float)y; idx = vge->ops->intsect(vg, vge, &ix, &iy); if (idx < closest_idx) { closest_idx = idx; closest_vge = vge; } } } if (closest_vge != NULL && closest_idx < AG_FLT_MAX-2) { closest_vge->mouseover = 1; } } } int RG_SketchWheel(RG_Tileview *tv, RG_TileElement *tel, int which) { if (tv->cur_tool != NULL && tv->cur_tool->flags & TILEVIEW_SKETCH_TOOL) { const RG_TileviewSketchToolOps *ops = (const RG_TileviewSketchToolOps *)tv->cur_tool->ops; if (ops->mousewheel != NULL) return (ops->mousewheel(tv->cur_tool, tel->tel_sketch.sk, which)); } return (0); } void RG_SketchKeyDown(RG_Tileview *tv, RG_TileElement *tel, int keysym, int keymod) { RG_Sketch *sk = tel->tel_sketch.sk; VG *vg = sk->vg; VG_Element *vge, *nvge; if (tv->cur_tool != NULL && tv->cur_tool->flags & TILEVIEW_SKETCH_TOOL) { const RG_TileviewSketchToolOps *ops = (const RG_TileviewSketchToolOps *)tv->cur_tool->ops; if (ops->keydown != NULL) { ops->keydown(tv->cur_tool, sk, keysym, keymod); return; } } switch (keysym) { case AG_KEY_DELETE: for (vge = TAILQ_FIRST(&vg->vges); vge != TAILQ_END(&vg->vges); vge = nvge) { nvge = TAILQ_NEXT(vge, vges); if (vge->selected) { RG_SketchUnselect(tv, tel, vge); VG_DestroyElement(vg, vge); } } break; } } void RG_SketchKeyUp(RG_Tileview *tv, RG_TileElement *tel, int keysym, int keymod) { RG_Sketch *sk = tel->tel_sketch.sk; if (tv->cur_tool != NULL && tv->cur_tool->flags & TILEVIEW_SKETCH_TOOL) { const RG_TileviewSketchToolOps *ops = (const RG_TileviewSketchToolOps *)tv->cur_tool->ops; if (ops->keyup != NULL) { ops->keyup(tv->cur_tool, sk, keysym, keymod); return; } } } static void SelectTool(AG_Event *event) { RG_Tileview *tv = AG_PTR(1); RG_TileviewTool *tvt = AG_PTR(2); RG_TileviewSelectTool(tv, tvt); } static void SelectToolFromToolbar(AG_Event *event) { AG_Button *btn = AG_SELF(); AG_Toolbar *tbar = AG_PTR(1); RG_Tileview *tv = AG_PTR(2); RG_TileviewTool *tvt = AG_PTR(3); AG_ToolbarSelectOnly(tbar, btn); if (tv->cur_tool == tvt) { RG_TileviewUnselectTool(tv); } else { RG_TileviewSelectTool(tv, tvt); } } void RG_SketchOpenMenu(RG_Tileview *tv, int x, int y) { RG_Sketch *sk = tv->tv_sketch.sk; AG_Menu *me; AG_MenuItem *mi; if (tv->tv_sketch.menu != NULL) RG_SketchCloseMenu(tv); me = tv->tv_sketch.menu = AG_MenuNew(NULL, 0); mi = tv->tv_sketch.menu_item = AG_MenuNode(me, NULL, NULL); { RG_TileviewTool *tvt; AG_MenuItem *m_tool; m_tool = AG_MenuAction(mi, _("Tools"), rgIconEdit.s, NULL,NULL); TAILQ_FOREACH(tvt, &tv->tools, tools) { if ((tvt->flags & TILEVIEW_SKETCH_TOOL) == 0) { continue; } AG_MenuAction(m_tool, _(tvt->ops->name), (tvt->ops->icon != NULL) ? tvt->ops->icon->s : NULL, SelectTool, "%p,%p", tv, tvt); } AG_MenuSeparator(mi); AG_MenuIntFlags(mi, _("Show sketch origin"), rgIconOrigin.s, &sk->vg->flags, VG_VISORIGIN, 0); AG_MenuIntFlags(mi, _("Show sketch grid"), rgIconGrid.s, &sk->vg->flags, VG_VISGRID, 0); AG_MenuIntFlags(mi, _("Show sketch extents"), NULL, &sk->vg->flags, VG_VISBBOXES, 0); AG_MenuSeparator(mi); RG_TileviewGenericMenu(tv, mi); } tv->tv_sketch.menu->itemSel = mi; tv->tv_sketch.menu_win = AG_MenuExpand(tv, me, mi, x, y); } void RG_SketchCloseMenu(RG_Tileview *tv) { AG_Menu *me = tv->tv_sketch.menu; AG_MenuItem *mi = tv->tv_sketch.menu_item; AG_MenuCollapse(me, mi); AG_ObjectDestroy(me); tv->tv_sketch.menu = NULL; tv->tv_sketch.menu_item = NULL; tv->tv_sketch.menu_win = NULL; } AG_Toolbar * RG_SketchToolbar(RG_Tileview *tv, RG_TileElement *tel) { AG_Toolbar *tbar; RG_TileviewTool *tvt; tbar = AG_ToolbarNew(tv->tel_box, AG_TOOLBAR_VERT, 1, AG_TOOLBAR_STICKY); TAILQ_FOREACH(tvt, &tv->tools, tools) { if ((tvt->flags & TILEVIEW_SKETCH_TOOL) == 0) { continue; } AG_ToolbarButtonIcon(tbar, tvt->ops->icon != NULL ? tvt->ops->icon->s : NULL, 0, SelectToolFromToolbar, "%p,%p,%p", tbar, tv, tvt); } return (tbar); }