
@blerner I see what you mean (about the picts produced by MetaPict). I need to think about, whether a change is doable. The idea of manually setting the overall picture width and height comes from MetaPost, where the goal is to produce images with a fixed size to be used in pdfs.

@blerner This might be easier to fix than I thought. I have a hunch, that the bounding box stems from line 22 in the definition of draw
. Changing this to either an user provided pict or an “empty pict” with no bounding box ought be easy. https://github.com/soegaard/metapict/blob/master/metapict/draw.rkt#L22

Nope. That’s not enough. Every place where dc
is used needs to me changed.

something mysterious went wrong with the pkg build last night at northwestern, but the snapshots appear to be good

(Since @ben asked yesterday)

@soegaard2 I think it’s not soooo bad — every place that (curve-pict-width)
or -height
are mentioned in draw.rkt should instead call a hypothetical (compute-curve-bbox c)
function (with variants for -curves, -bezier, etc). The hard part is that the draw
function itself presumes that all its subpicts are cc-superimpose
d and all the same size. But that to me seems ok: Suppose that draw
computed the bboxes of all its arguments, took the maximum of them, and then cc-superimposed over that. Then a programmer can write (fold my-combiner (map draw my-imgs))
to get individual tight bboxes for each img, or they can write (draw my-imgs)
to pad each img to the same size and then cc-superimpose
them all at once.

One finessed issue that occurred to me is, how to handle arrowheads. The case I’m thinking of is, suppose I have a C-shaped curve with arrows at both tips. Then I’d really, really like to be able to use rt-find
or rb-find
to coincide with the endpoints of the curve. But because of the vertical bulk of the arrowhead, a simple bounding-box would go beyond those corners, and so the finder functions would be useless. Two possibilities occur to me:

1) You could be totally awesome and build out TikZ’s notion of named anchors within an image :wink: But that’s a lot of work…

2) You draw the image as normal, including the arrow tips. Then you compute a second bounding box that ignores all path tip decorations, and you a variant of @florence’s trick to change the bbox to that: (let ([tight-bb (blank tight-width tight-height)]) (refocus (*-superimpose image-with-tips tight-bb) tight-bb))
, (where *-superimpose
is whichever function allows you the easiest way to place the two boxes in the right placement relative to each toher). Now, the pict you return will draw everything but claim a tighter bounding box, and yet calling panorama
on it will expand to include the arrow tips

@blerner I need to think it through. Wrt to 1). Maybe the stuff in "node.rkt: is what you have in mind? It contains a work-in-progress. The idea is to have a convenient way of drawing nodes and connecting them. The shape of a node can be user-defined, but the most common ones are provided. Anchors on the node is computed by (anchor n v)
where n is a node and v is a vector (in the direction you want the anchor). So for example (anchor (circle-node (pt 2 3) .1) right)
will give you the point in the circle to the right of the center.

It needs to support more of the features in tikz though.

I’ll try to look later today, need to prep for lectures, first :-)

@alexknauth Following what you teached me, I got inspired to write this SO question addressing the issue of overriding a struct: https://stackoverflow.com/questions/55262818/how-to-override-a-struct-constructor-while-still-providing-the-struct-metadata-f

I wonder if my reasoning is correct, so I’ll just leave that here.

Yes, this is related to what I was thinking. Also things like (point-of c t)
, but I don’t know how to make that interact with the find-*
operations of picts. Rampant speculation ahead:

Maybe create a define--curve-and-anchors
macro, such that (define-curve-and-anchors my-curve-name [(my-anchor-name1 (point-at curve-exp t1)) (my-anchor-name2 (some-other-coord ...)) ...])
would produce a bunch of bindings of my-anchor-name*
to zero-sized (blank)
s that were pinned to their respective locations on the result of curve-exp
, and the resulting mess of pinned picts is bound to my-curve-name
. Then, you could easily say (cc-find my-curve-name my-anchor-name1)
to get the exact point of that anchor relative to the curve, no matter where the curve got embedded in another pict

(Basically, I’m trying to get around the fact that cc-find
and its kin are mutually recursive and not open to extension, so the only way to add anchors to an existing pict is to overlay (blank)
s onto that pict, and then find them later.)

So, my-curve-name
would be bound to something like (refocus (pin-over the-curve the-blanks) the-curve)

Would tagged picts help you at all here?
https://docs.racket-lang.org/ppict/index.html#%28def._%28%28lib._ppict%2Ftag..rkt%29._tag-pict%29%29

ooh that’s fun. so if you tagged all curves, then you could use find-tag to get them out of the surrounding pict, then somehow combine the results of ht-find
and point-of
to get an anchor point on the curve…

actually, ppict in general looks pretty neat

I actually originally used that adding a blank pict trick then refocusing trick in conjunction with tags and at-find-pict to place things in exact locations and then draw arrows between them!

Also you might find this little ditty useful:
(define (arrow/tag* src find-src dest find-dest [arw 30]
#:line-width [line-width 5]
#:start-angle [start-angle #f]
#:end-angle [end-angle #f]
#:solid? [solid? #t])
(lambda (pict)
(pin-arrow-line
arw
pict
(find-tag pict src) find-src (find-tag pict dest) find-dest
#:line-width line-width
#:start-angle start-angle
#:end-angle end-angle
#:solid? solid?)))
Its just pin-arrow-line
but it a) takes tags not picts and b) its curried so you can build up all the arrows you want to make then fold them all in a separate step (which I found cleans up the code a lot)

it’s great other than the earlier failing of pin-arrow-line
to give me proper bounding boxes :wink:

seems like we should just fix that

I sure wouldn’t say no to that. But it does sound like there’s a broader library of curve-based pict builders that could easily exist, and metapict is a good start on them…

@blerner re the panorama and rotate issue: https://github.com/racket/pict/pull/51

path%
does expose its bounding box, so this shouldn’t be to hard to do. Should we add new functions that draw arrows and extend the bounding box, or should we add yet one more optional argument to pin-arrow-line
and friends?

one more optional argument

n more functions is so much added complexity

Consider get-path-bounding-box

Sounds like a plan. I’ll try to add it later today

@robby Why doesn’t ->
’s val first projection raise blame errors for first-order, positive violations before receiving the negative party? It seems to do all the work of checking for such violations before receiving the missing party, so it’s clearly taking advantage of not having to do that work twice, but I’m unclear on why it defers actually signaling the error. Is there some implicit protocol that val first projections aren’t supposed to raise errors until given the missing party?

(If the answer is just “it was easier to implement that way”, that’s fine; I’m mostly just trying to understand if signaling an error earlier in a custom contract’s val first projection would be considered bad.)

It is an explicit protocol

Not an implicit one.

Oh? Where is it documented?

Rereading your question I think that maybe I actually don’t understand it. Certainly contracts do not, unless there are bugs, go to lengths to compute something and then not use it for ease of implementation…..

And the protocols are explicit. Which I don’t think is synonymous with documented although I understand why you might think that.

hrm, this is a little tricky. get-bounding-box
from dc-path%
gives a bounding box that contains the bezier control points, not just the path. Also the bit that adds the arrow head uses picts (undocumented) internal drawing language, and I haven’t the foggiest how to go about convincing this to extend the bounding box in the right way. Also panorama behaves oddly on the result of pin-arrow-line
, extending the bounding box in places where nothing is actually drawn…

so in short I haven’t the foggiest how to do this without adding a bunch of extra whitespace

get-path-bounding-box from dc% ought to give a tighter bound - but do you have the drawing context?

@robby Okay, let me rephrase with an example, since I get the sense that I didn’t make my question very clear. I was intrigued that this program does not raise an exception: #lang racket
(require racket/contract/private/blame
syntax/location)
(define ctc (-> any/c))
(define blame (make-blame (quote-srcloc) 'foo (λ () (contract-name ctc)) 'pos #f #t))
(((contract-val-first-projection ctc) blame) 'not-a-function)
I found it interesting because I know that ->
implements its own val first projection, so it wasn’t just using the adapter around the late neg projection that would wait until all the arguments had been supplied to raise an error. I looked at the implementation of ->
’s val first projection, and I saw that when the first-order check fails, even if it has all the blame information necessary to report an error, it explicitly waits to do so until the negative party is provided. The rest of the projection does nothing at all except call raise-blame-error
, like this: (unless (procedure? val)
(k
(λ (neg-party)
(raise-blame-error blame #:missing-party neg-party val
`(expected: ,proc/meth
given: "~e")
val))))
So I was wondering: if I were to define my own contract, with its own val first projection, am I expected to wait until I get the negative party until I call raise-blame-error
? And if so, why, and is it documented anywhere?

Oh! I see my confusion — I somehow confused late-neg and val-first.

The answer is: don’t define any val-first projections.

that’s there only for legacy reasons

But yes, the protocol for val-first is not to raise blame until you have all of the blam einformation.

There is some case where the literal interpretation of that isn’t followed (ie positive blame may be raised in a server module before it gets required but tht’s a separate issue)

Ah yes I do. That might fixe the first problem, let me try

Is val first really only for legacy uses, though? I imagine it doesn’t matter that much for most contracts, but it seems like it could matter for ->
(since it’s used so often). Since contract-out
has the value but not the negative party inside the providing module, ->
’s val first projection skips performing the first order checks every time it’s used in a different module (and therefore has a different negative party).

If it only provided a late neg projection, it’d have to do those first order checks again inside every importing module.

val first really is only for legacy, yes.

it should be avoided

Maybe my bigger question is why val first projections are deprecated at all if most contracts are attached with contract-out
, anyway… which really does get the value before it gets the negative party, so it has to do the currying anyway.

What does using late neg projections save over using val first projections?

it doesn’t do currying

it gets called twice

Sure, but it still closes over the blame object, so don’t you end up having to do the same amount of allocation? Or is the point that you don’t have a lingering reference to val
in the closure…? I guess I’m confused, but it doesn’t really matter, I was mostly just curious.

oh no i take it back i don’t have the dc
there…

I guess I could see that, for subcontracts where the blame is swapped, you lose with the val first projection because you have to apply the curried function, and you already have the negative party at that point.

Okay - so the question is whether we can get a tighter bounding box for a path (without knowing the pen size).

Maybe there is something low-level in Cairo?

The pen size is known at this point

the currying with late-neg isn’t helpful; might as well just take the two arguments at once

It looks like get-path-bounding-box
is actually implemented as a low level cairo operation

(in path%)

But it is helpful for the top-level projection of a contract inside contract-out
, right? Since when a contract is attached to a value with contract-out
, you do have the value but not the negative party. If that isn’t true, why does contract-out
use the val first projection instead of the late neg projection?

Yes - I think I was partly responsible for that one.


But I’m rather heistent to bring something that low-level into pict directly

?

huh same. whomp

not really

top-level doesn’t matter

side request: can we document the deprecation of val-first projections more explicitly?

it doesn’t happen in a loop (in most programs)

@notjack that sounds like a great idea

but the other stuff does

(happen in a loop)

I could always make a record-dc%
or something like that and call get-path-bounding-box
directly on that

Sounds expensive.

Okay, yeah, I’ll buy that. But then why bother having a val first projection on ->
at all? And why bother using the val first projection inside the implementation of contract-out
, with a big comment explaining that it’s a legitimate use (which I don’t actually understand)? (define p (parameterize ([warn-about-val-first? #f])
;; when we're building the val-first projection
;; here we might be needing the plus1 arity
;; function (which will be on the val first's result)
;; so this is a legtimate use. don't warn.
(get/build-val-first-projection ctc)))

we can delete it

(that’s the royal we, of course)

Okay, I’m happy with that answer. :)

There is some code that still uses val-first projections and for that code, it is nice to have a proper val-first ->

Did it exist before late neg contracts were added?


Ah

It would be best to get rid of all val-first projections. The hangers on are, I believe, the opt/c
contracts.

Is cairo_path_extents
what get-path-bounding-box
uses?

so they should probably be fixed before removing that

and probalby it would be good to set up something like drdr but for the gradual typing benchmark too

so to make sure the change doesn’t have unintended consequences

But yes, @notjack, val-first existed before late-neg (and #:projection
before either)

sometimes

(define cairo_op
(cond
[(eq? type 'path) cairo_path_extents]
[(eq? type 'fill) cairo_fill_extents]
[(eq? type 'stroke) cairo_stroke_extents]
[else (error 'get-tight-binding-boc "expected 'path, 'fill, or, 'stroke")]))

Yeah. One can see the original function name in the error message :slightly_smiling_face:

this is a tangent, but how can I make a code searching tool for racket that’s 1) binding and macro aware and 2) multi-module + multi-package?

I’m guessing there have been a couple of attempts at that kind of thing before

lol

Could we make a new, scratch drawing context with a pen size 1 and use that as the cr
(cairo “context”)?

What do you mean by searching in a “macro-aware” way? Do you mean that if I had a macro def and call like this: (define-simple-macro (m x) (f x))
(m (g 5))
Searching for (f (g 5))
would succeed?

Thats what I meant by using a record-dc%
or something

Possibly, yes. But really I’m only thinking of searching for usages of single identifiers, not for patterns of usages.

I can create one once an reuse for all the pin-arrow calls

(at least, as a first draft)

but IDK what will happen if the curve extends outside of the dc’s bounds

Ah. Does a record-dc% even have a cairo backing?

it does

great

but i think I can just use an empty bitmap-dc instead

Some specific things I’d like to handle are modules that reprovide and rename the exports of other modules and macros that produce usages of things

And I’d like to be able to search across the source code of all packages in the entire package catalog for usages

yeah, it looks like the backing DC needs to be big enough to draw the entire path already

that’s annoying

Oooh, would it “purposefully not find” anything if you were searching for add1
in this: (define-simple-macro (m x e) (let ([x 5]) e))
(m add1 add1)

Yes, I definitely think it shouldn’t count that as a usage of add1

Very good example

Should it find anything in a situation like this? (define-simple-macro (m x y z)
#:with yz/x (format-id #'x "~a~a" #'y (syntax-e #'z))
(yz/x 5))
(m here add 1)

It’s not not a usage of add1. And multiple occurrences of (m here add 1)
is definitely a different case than just one occurrence or no occurrences at all. But I don’t know how to report it meaningfully to a user.

and I think its different if the definition of m
is in its own module and imported by many modules that each write (m here add 1)
, because there needs to be some way to tell the user that although add1 is being used in each of these client modules, they’re all the result of the same macro

Now what about (define-simple-macro (m x y z) #:with yz/x (if (today-is-tuesday?) (format-id #’x “~a~a” #’y (syntax-e #’z)) #’sub1) (yz/x 5)) (m here add 1)

Yeah that should still count. Macro implementations using nondeterminism / IO are a case of caveat emptor. If the expanded code is nondeterministic, then it seems logical to me that the usages found in expanded code are nondeterministic.

Okay. Now what about match-expanders where you use the pattern form but not the expression form? Like searching for uses of m
: (define-match-expander m
(syntax-parser [(_) #'5])
(syntax-parser [(_) #'5]))
In: (match (+ 2 3)
[(m) #true]
[_ #false])
Both in the version of match that uses the 'origin
and 'disappeared-use
properties to save this information, and in a hypothetical version of match that didn’t save those properties.

If you’re searching for usages of m
then both forms should count as usages. I think there should be some way to name the two forms of m
. I made a syntax annotations library that did this kind of thing by forcing all such “multi-meaning” identifiers to be implemented as rename transformers that can be configured to expand to different bindings. In this context that would mean that m
would be something like a struct with two fields, each of which contains an identifier. The first is used for expressions and the second for patterns. So instead of searching for m
, you’d search for one of those two identifiers contained within m
if you wanted to search for usages of just one of the two forms of m
.

The problem with this approach to searching is that identifiers aren’t serializable. So how do you build and save a search index?

is there a good way to get scribble
to spit out xelatex sources for a document? the --xelatex
flag has it try to generate a pdf, which is not very helpful for debugging.

@matias you should be able to generate latex sources with --latex
and then compile that with xelatex

@samth yes, but that uses the latex pdf rendering mixin and not the xelatex one, right?

the only difference between those is what command is used

see the two functions here: https://github.com/racket/scribble/blob/master/scribble-lib/scribble/pdf-render.rkt

I’m trying to (re)build racket form source on a Mac, but I’ve started getting a fun error:
gcc -o racketcgc main.o -Wl,-headerpad_max_install_names -F. -framework Racket -ldl -lm -liconv
Undefined symbols for architecture x86_64:
"_GC_malloc", referenced from:
_main_after_stack in main.o
(plus lots of other undefined symbols)
Anyone have any idea how to go about debugging this?

ah, somehow the wrong version of ar
was in my path… fun…