soegaard2
2019-3-20 10:27:49

@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.


soegaard2
2019-3-20 11:09:07

@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


soegaard2
2019-3-20 11:21:24

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


robby
2019-3-20 11:38:56

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


robby
2019-3-20 11:39:07

(Since @ben asked yesterday)


blerner
2019-3-20 11:47:09

@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-superimposed 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.


blerner
2019-3-20 11:49:32

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:


blerner
2019-3-20 11:49:52

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…


blerner
2019-3-20 11:53:37

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


soegaard2
2019-3-20 13:50:24

@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.


soegaard2
2019-3-20 13:52:18

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


blerner
2019-3-20 13:54:02

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


jerome.martin.dev
2019-3-20 14:52:09

@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


jerome.martin.dev
2019-3-20 14:53:54

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


blerner
2019-3-20 15:50:39

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:


blerner
2019-3-20 15:54:46

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


blerner
2019-3-20 15:56:03

(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.)


blerner
2019-3-20 15:56:51

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



blerner
2019-3-20 16:50:58

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…


blerner
2019-3-20 16:53:20

actually, ppict in general looks pretty neat


florence
2019-3-20 17:04:30

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!


florence
2019-3-20 17:07:10

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)


blerner
2019-3-20 17:10:10

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


samth
2019-3-20 17:45:20

seems like we should just fix that


blerner
2019-3-20 17:46:11

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…


florence
2019-3-20 17:54:40

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


florence
2019-3-20 18:17:55

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?


samth
2019-3-20 18:19:13

one more optional argument


samth
2019-3-20 18:19:23

n more functions is so much added complexity


soegaard2
2019-3-20 18:20:23

Consider get-path-bounding-box


florence
2019-3-20 18:20:52

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


lexi.lambda
2019-3-20 19:43:55

@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?


lexi.lambda
2019-3-20 19:46:08

(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.)


robby
2019-3-20 20:11:44

It is an explicit protocol


robby
2019-3-20 20:11:58

Not an implicit one.


lexi.lambda
2019-3-20 20:13:22

Oh? Where is it documented?


robby
2019-3-20 20:26:16

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…..


robby
2019-3-20 20:26:52

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


florence
2019-3-20 20:38:12

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…


florence
2019-3-20 20:38:31

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


soegaard2
2019-3-20 20:39:41

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


lexi.lambda
2019-3-20 20:39:49

@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?


robby
2019-3-20 20:40:57

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


robby
2019-3-20 20:41:06

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


robby
2019-3-20 20:41:13

that’s there only for legacy reasons


robby
2019-3-20 20:41:36

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


robby
2019-3-20 20:42:10

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)


florence
2019-3-20 20:42:17

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


lexi.lambda
2019-3-20 20:42:56

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).


lexi.lambda
2019-3-20 20:43:39

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


robby
2019-3-20 20:44:31

val first really is only for legacy, yes.


robby
2019-3-20 20:44:37

it should be avoided


lexi.lambda
2019-3-20 20:44:51

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.


lexi.lambda
2019-3-20 20:45:06

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


robby
2019-3-20 20:45:12

it doesn’t do currying


robby
2019-3-20 20:45:16

it gets called twice


lexi.lambda
2019-3-20 20:47:08

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.


florence
2019-3-20 20:47:40

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


lexi.lambda
2019-3-20 20:48:10

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.


soegaard2
2019-3-20 20:48:33

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


soegaard2
2019-3-20 20:48:44

Maybe there is something low-level in Cairo?


florence
2019-3-20 20:49:00

The pen size is known at this point


robby
2019-3-20 20:49:01

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


florence
2019-3-20 20:49:26

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


florence
2019-3-20 20:50:16

(in path%)


lexi.lambda
2019-3-20 20:50:24

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?


soegaard2
2019-3-20 20:50:35

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


soegaard2
2019-3-20 20:50:50

I get a “connection refused” from https://www.cairographics.org/


florence
2019-3-20 20:50:50

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


soegaard2
2019-3-20 20:50:51

?


florence
2019-3-20 20:51:26

huh same. whomp


robby
2019-3-20 20:51:45

not really


robby
2019-3-20 20:51:49

top-level doesn’t matter


notjack
2019-3-20 20:51:52

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


robby
2019-3-20 20:51:58

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


robby
2019-3-20 20:52:04

@notjack that sounds like a great idea


robby
2019-3-20 20:52:20

but the other stuff does


robby
2019-3-20 20:52:23

(happen in a loop)


florence
2019-3-20 20:53:26

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


soegaard2
2019-3-20 20:53:41

Sounds expensive.


lexi.lambda
2019-3-20 20:53:58

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)))


robby
2019-3-20 20:54:25

we can delete it


robby
2019-3-20 20:54:32

(that’s the royal we, of course)


lexi.lambda
2019-3-20 20:54:48

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


robby
2019-3-20 20:55:15

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


notjack
2019-3-20 20:55:21

Did it exist before late neg contracts were added?



notjack
2019-3-20 20:55:24

Ah


robby
2019-3-20 20:55:47

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


soegaard2
2019-3-20 20:55:50

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


robby
2019-3-20 20:55:56

so they should probably be fixed before removing that


robby
2019-3-20 20:56:15

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


robby
2019-3-20 20:56:25

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


robby
2019-3-20 20:56:51

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


florence
2019-3-20 20:56:58

sometimes


florence
2019-3-20 20:57:06
(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")]))

soegaard2
2019-3-20 20:58:03

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


notjack
2019-3-20 20:58:31

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?


notjack
2019-3-20 20:59:21

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


florence
2019-3-20 20:59:51

lol


soegaard2
2019-3-20 21:00:40

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


alexknauth
2019-3-20 21:02:49

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?


florence
2019-3-20 21:03:31

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


notjack
2019-3-20 21:03:46

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


florence
2019-3-20 21:03:54

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


notjack
2019-3-20 21:03:59

(at least, as a first draft)


florence
2019-3-20 21:04:21

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


soegaard2
2019-3-20 21:05:18

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


florence
2019-3-20 21:05:40

it does


soegaard2
2019-3-20 21:05:45

great


florence
2019-3-20 21:05:57

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


notjack
2019-3-20 21:06:29

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


notjack
2019-3-20 21:07:54

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


florence
2019-3-20 21:16:13

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


florence
2019-3-20 21:16:18

that’s annoying


alexknauth
2019-3-20 21:18:18

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)


notjack
2019-3-20 21:20:11

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


notjack
2019-3-20 21:20:20

Very good example


alexknauth
2019-3-20 21:33:26

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)


notjack
2019-3-20 21:36:19

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.


notjack
2019-3-20 21:37:39

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


alexknauth
2019-3-20 21:37:47

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)


notjack
2019-3-20 21:39:34

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.


alexknauth
2019-3-20 21:47:02

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.


notjack
2019-3-20 21:51:15

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.


notjack
2019-3-20 21:51:43

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


matias
2019-3-21 00:30:02

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.


samth
2019-3-21 00:45:23

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


matias
2019-3-21 00:55:10

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


samth
2019-3-21 00:57:58

the only difference between those is what command is used



florence
2019-3-21 02:30:20

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?


florence
2019-3-21 02:41:34

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