Skip to content

Commit

Permalink
Merge the "diamond" enhancement to trunk.
Browse files Browse the repository at this point in the history
FossilOrigin-Name: bc3bd914a386c504de0ab4862c0460d1ff287360567766e8889ad3d1e93afcd1
  • Loading branch information
drh committed Feb 8, 2024
2 parents c56b66f + 6627109 commit 4667608
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 4 deletions.
91 changes: 91 additions & 0 deletions doc/diamondobj.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Diamond objects

A diamond acts much like a [box](./boxobj.md) except that its corners
are rotated around the center point such that they become the shape’s
four primary cardinal points:

~~~~ pikchr indent
D: diamond "Cardinal" "Points"
dot ".n" above at D.n
dot " .e" ljust at D.e
dot ".s" below at D.s
dot ".w " rjust at D.w
~~~~

Indeed, before Pikchr [got this primitive](/info/36751abee2), the
workaround was to draw an invisible box to hold the text, then draw
lines between its cardinal points:

~~~~ pikchr indent
box width 150% invis "“Diamond”" "Label"
line from last.w to last.n to last.e to last.s close
~~~~

This does work, and it has the advantage of being compatible with the
original PIC and with GNU `dpic`, but it also has a number of
weaknesses, one of which is evident in comparing the examples above: the
labels aren’t as well-centered when manually drawing the diamond.

Another is the need for that 150% fudge factor to the invisible box’s
width, without which the labels would be truncated by the dimensions
Pikchr calculates for the invisible bounding box:

~~~~ pikchr indent
box invis "“Diamond”" "Label"
line from last.w to last.n to last.e to last.s close
~~~~

A third advantage falls out of this fact: the “`fit`” attribute works as
expected for Pikchr diamonds. It cannot with the manual PIC-compatible
workaround due to the lack of a properly-calculated bounding box, one taking
into account the rotated cardinal points:

~~~~ pikchr indent
text "Unfitted:"
diamond "D"
text "Properly fitted:"
diamond "D" fit
text "Badly fitted:"
box invis "D" fit
line from last.w to last.n to last.e to last.s close
~~~~

There’s a fourth, more subtle advantage to having this primtive built
into the language: the location of the ordinal points is now
well-defined:

~~~~ pikchr indent
D: diamond "Ordinal" "Points"
dot " .ne" ljust above at D.ne
dot " .se" ljust below at D.se
dot ".sw " rjust below at D.sw
dot ".nw " rjust above at D.nw
~~~~

To replicate that with the PIC-compatible hack above, you’d have to do
the geometry to work out where those points land along the lines. It’s
better to leave that bit of tedious math to the Pikchr renderer.

Unlike a box, you cannot currently round the corners on a diamond. You
can, however, programmatically override the default height and width
by redefining the `diamondht` and `diamondwid` variables. Here we show
two different ways of making the diamond 25% larger:

~~~~ pikchr indent
D: diamond thick "Diamond" "Dimensions" width 125% height 125%
X1: line thin color gray left 70% from 4mm left of (D.w,D.n)
X2: line same from 4mm left of (D.w,D.s)
text "height" small at 1/2 way between X1 and X2
line thin color gray from previous text.n up until even with X1 ->
line thin color gray from previous text.s down until even with X2 ->
X3: line thin color gray down 50% from 2mm below (D.w,D.s)
X4: line same from 2mm below (D.e,D.s)
text "width" small at 1/2 way between X3 and X4
line thin color gray from previous text.w left until even with X3 ->
line thin color gray from previous text.e right until even with X4 ->
diamondht = diamondht * 1.25
diamondwid = diamondwid * 1.25
diamond thick "Diamond" "Dimensions" at 1.5in right of D
~~~~
4 changes: 3 additions & 1 deletion doc/differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ in PIC.
~~~ pikchr indent
oval "oval"
move
diamond "diamond"
move
cylinder "cylinder"
move
file "file"
Expand Down Expand Up @@ -173,7 +175,7 @@ The "`chop`" attribute is completely redesigned. It takes no
argument and can only appear once. If "`chop`" is specified on
a line (or arrow or spline) then end-points of the line that
would have landed on the center of a box-like object (box,
circle, cylinder, ellipse, file, or oval) are shortened to
circle, cylinder, diamond, ellipse, file, or oval) are shortened to
land exactly on the border of that object.

~~~ pikchr indent
Expand Down
1 change: 1 addition & 0 deletions doc/grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ A complete input file to Pikchr consists of a single *statement-list*.
* **box** [▶info](./boxobj.md)
* **circle** [▶info](./circleobj.md)
* **cylinder** [▶info](./cylinderobj.md)
* **diamond** [▶info](./diamondobj.md)
* **dot**
* **ellipse** [▶info](./ellipseobj.md)
* **file** [▶info](./fileobj.md)
Expand Down
2 changes: 1 addition & 1 deletion doc/locattr.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# location-attribute

A *location-attribute* is an attribute used to assign a location to
a block object (box, circle, cylinder, dot, ellipse, file, oval, or text).
a block object (box, circle, cylinder, diamond, dot, ellipse, file, oval, or text).
If a *location-attribute* appears on a line object (arc, arrow, line, move,
or spline) an error is issued and processing stops.

Expand Down
2 changes: 1 addition & 1 deletion doc/pathattr.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A *path-attribute* is used to provide the origin and direction of a line
object, that being an arc, arrow, line, move, or spline. It is an error
to use a *path-attribute* on a block object, that being a box, circle,
cylinder, dot, ellipse, file, oval, or text.
cylinder, diamond, dot, ellipse, file, oval, or text.

There are seven forms:

Expand Down
67 changes: 66 additions & 1 deletion pikchr.c
Original file line number Diff line number Diff line change
Expand Up @@ -3579,6 +3579,8 @@ static const struct { const char *zName; PNum val; } aBuiltin[] = {
{ "cylrad", 0.075 },
{ "cylwid", 0.75 },
{ "dashwid", 0.05 },
{ "diamondht", 0.75 },
{ "diamondwid", 1.0 },
{ "dotrad", 0.015 },
{ "ellipseht", 0.5 },
{ "ellipsewid", 0.75 },
Expand Down Expand Up @@ -3959,6 +3961,58 @@ static void dotRender(Pik *p, PObj *pObj){
pik_append_txt(p, pObj, 0);
}

/* Methods for the "diamond" class */
static void diamondInit(Pik *p, PObj *pObj){
pObj->w = pik_value(p, "diamondwid",10,0);
pObj->h = pik_value(p, "diamondht",9,0);
}
/* Return offset from the center of the box to the compass point
** given by parameter cp */
static PPoint diamondOffset(Pik *p, PObj *pObj, int cp){
PPoint pt = cZeroPoint;
PNum w2 = 0.5*pObj->w;
PNum w4 = 0.25*pObj->w;
PNum h2 = 0.5*pObj->h;
PNum h4 = 0.25*pObj->h;
switch( cp ){
case CP_C: break;
case CP_N: pt.x = 0.0; pt.y = h2; break;
case CP_NE: pt.x = w4; pt.y = h4; break;
case CP_E: pt.x = w2; pt.y = 0.0; break;
case CP_SE: pt.x = w4; pt.y = -h4; break;
case CP_S: pt.x = 0.0; pt.y = -h2; break;
case CP_SW: pt.x = -w4; pt.y = -h4; break;
case CP_W: pt.x = -w2; pt.y = 0.0; break;
case CP_NW: pt.x = -w4; pt.y = h4; break;
default: assert(0);
}
UNUSED_PARAMETER(p);
return pt;
}
static void diamondFit(Pik *p, PObj *pObj, PNum w, PNum h){
if( pObj->w>0 && pObj->h>0 ){
PNum x = pObj->w*h/pObj->h + w;
PNum y = pObj->h*x/pObj->w;
pObj->w = x;
pObj->h = y;
}
UNUSED_PARAMETER(p);
}
static void diamondRender(Pik *p, PObj *pObj){
PNum w2 = 0.5*pObj->w;
PNum h2 = 0.5*pObj->h;
PPoint pt = pObj->ptAt;
if( pObj->sw>=0.0 ){
pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y);
pik_append_xy(p,"L", pt.x,pt.y-h2);
pik_append_xy(p,"L", pt.x+w2,pt.y);
pik_append_xy(p,"L", pt.x,pt.y+h2);
pik_append(p,"Z\" ",-1);
pik_append_style(p,pObj,3);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pObj, 0);
}


/* Methods for the "ellipse" class */
Expand Down Expand Up @@ -4343,6 +4397,17 @@ static const PClass aClass[] = {
/* xFit */ cylinderFit,
/* xRender */ cylinderRender
},
{ /* name */ "diamond",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ diamondInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ diamondOffset,
/* xFit */ diamondFit,
/* xRender */ diamondRender
},
{ /* name */ "dot",
/* isline */ 0,
/* eJust */ 0,
Expand Down Expand Up @@ -8145,4 +8210,4 @@ int Pikchr_Init(Tcl_Interp *interp){
#endif /* PIKCHR_TCL */


#line 8173 "pikchr.c"
#line 8238 "pikchr.c"
65 changes: 65 additions & 0 deletions pikchr.y
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,8 @@ static const struct { const char *zName; PNum val; } aBuiltin[] = {
{ "cylrad", 0.075 },
{ "cylwid", 0.75 },
{ "dashwid", 0.05 },
{ "diamondht", 0.75 },
{ "diamondwid", 1.0 },
{ "dotrad", 0.015 },
{ "ellipseht", 0.5 },
{ "ellipsewid", 0.75 },
Expand Down Expand Up @@ -1352,6 +1354,58 @@ static void dotRender(Pik *p, PObj *pObj){
pik_append_txt(p, pObj, 0);
}

/* Methods for the "diamond" class */
static void diamondInit(Pik *p, PObj *pObj){
pObj->w = pik_value(p, "diamondwid",10,0);
pObj->h = pik_value(p, "diamondht",9,0);
}
/* Return offset from the center of the box to the compass point
** given by parameter cp */
static PPoint diamondOffset(Pik *p, PObj *pObj, int cp){
PPoint pt = cZeroPoint;
PNum w2 = 0.5*pObj->w;
PNum w4 = 0.25*pObj->w;
PNum h2 = 0.5*pObj->h;
PNum h4 = 0.25*pObj->h;
switch( cp ){
case CP_C: break;
case CP_N: pt.x = 0.0; pt.y = h2; break;
case CP_NE: pt.x = w4; pt.y = h4; break;
case CP_E: pt.x = w2; pt.y = 0.0; break;
case CP_SE: pt.x = w4; pt.y = -h4; break;
case CP_S: pt.x = 0.0; pt.y = -h2; break;
case CP_SW: pt.x = -w4; pt.y = -h4; break;
case CP_W: pt.x = -w2; pt.y = 0.0; break;
case CP_NW: pt.x = -w4; pt.y = h4; break;
default: assert(0);
}
UNUSED_PARAMETER(p);
return pt;
}
static void diamondFit(Pik *p, PObj *pObj, PNum w, PNum h){
if( pObj->w>0 && pObj->h>0 ){
PNum x = pObj->w*h/pObj->h + w;
PNum y = pObj->h*x/pObj->w;
pObj->w = x;
pObj->h = y;
}
UNUSED_PARAMETER(p);
}
static void diamondRender(Pik *p, PObj *pObj){
PNum w2 = 0.5*pObj->w;
PNum h2 = 0.5*pObj->h;
PPoint pt = pObj->ptAt;
if( pObj->sw>=0.0 ){
pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y);
pik_append_xy(p,"L", pt.x,pt.y-h2);
pik_append_xy(p,"L", pt.x+w2,pt.y);
pik_append_xy(p,"L", pt.x,pt.y+h2);
pik_append(p,"Z\" ",-1);
pik_append_style(p,pObj,3);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pObj, 0);
}


/* Methods for the "ellipse" class */
Expand Down Expand Up @@ -1736,6 +1790,17 @@ static const PClass aClass[] = {
/* xFit */ cylinderFit,
/* xRender */ cylinderRender
},
{ /* name */ "diamond",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ diamondInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ diamondOffset,
/* xFit */ diamondFit,
/* xRender */ diamondRender
},
{ /* name */ "dot",
/* isline */ 0,
/* eJust */ 0,
Expand Down
38 changes: 38 additions & 0 deletions tests/autochop10.pikchr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
C: diamond "diamond"

line from C to 3cm heading 00 from C chop;
line from C to 3cm heading 10 from C chop;
line from C to 3cm heading 20 from C chop;
line from C to 3cm heading 30 from C chop;
line from C to 3cm heading 40 from C chop;
line from C to 3cm heading 50 from C chop;
line from C to 3cm heading 60 from C chop;
line from C to 3cm heading 70 from C chop;
line from C to 3cm heading 80 from C chop;
line from C to 3cm heading 90 from C chop;
line from C to 3cm heading 100 from C chop;
line from C to 3cm heading 110 from C chop;
line from C to 3cm heading 120 from C chop;
line from C to 3cm heading 130 from C chop;
line from C to 3cm heading 140 from C chop;
line from C to 3cm heading 150 from C chop;
line from C to 3cm heading 160 from C chop;
line from C to 3cm heading 170 from C chop;
line from C to 3cm heading 180 from C chop;
line from C to 3cm heading 190 from C chop;
line from C to 3cm heading 200 from C chop;
line from C to 3cm heading 210 from C chop;
line from C to 3cm heading 220 from C chop;
line from C to 3cm heading 230 from C chop;
line from C to 3cm heading 240 from C chop;
line from C to 3cm heading 250 from C chop;
line from C to 3cm heading 260 from C chop;
line from C to 3cm heading 270 from C chop;
line from C to 3cm heading 280 from C chop;
line from C to 3cm heading 290 from C chop;
line from C to 3cm heading 300 from C chop;
line from C to 3cm heading 310 from C chop;
line from C to 3cm heading 320 from C chop;
line from C to 3cm heading 330 from C chop;
line from C to 3cm heading 340 from C chop;
line from C to 3cm heading 350 from C chop;
14 changes: 14 additions & 0 deletions tests/diamond01.pikchr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
D1: diamond "First Diamond"
arrow
D2: diamond "Above" above "Below" below
arrow
D3: diamond "Fitted" fit
arrow from D2.s down 1cm
down
D4: diamond "Long string above" above "Below1" below fit
right
arrow
D5: diamond width 300% "thin" fit
arrow from D3.e right 2cm
right
D6: diamond height 300% "tall" above "diamond" below fit
1 change: 1 addition & 0 deletions tests/test78.pikchr
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ file "File" fill lightgreen thickness 0 fit
oval "Oval 1" fill pink thickness 0 fit
oval "Oval 2" fill lightgrey width 1.1cm height 2cm thickness 0
cylinder "Cylinder" fill orange fit thickness 0
diamond "Diamond" fill lightblue fit thickness 0

0 comments on commit 4667608

Please sign in to comment.